appJar module
# -*- coding: utf-8 -*- #from appJar.appjar import gui from .appjar import gui __all__ = ['gui']
Classes
class gui
Class to represent the GUI - Create one of these - add some widgets - call the go() function
class gui(object): """ Class to represent the GUI - Create one of these - add some widgets - call the go() function """ # ensure only one instance of gui is created # set to True in constructor # set back to false in stop() instantiated = False built = False # static variables exe_file = None exe_path = None lib_file = None lib_path = None # globals for supported platforms WINDOWS = 1 MAC = 2 LINUX = 3 # positioning N = N NE = NE E = E SE = SE S = S SW = SW W = W NW = NW CENTER = CENTER LEFT = LEFT RIGHT = RIGHT # reliefs SUNKEN = SUNKEN RAISED = RAISED GROOVE = GROOVE RIDGE = RIDGE FLAT = FLAT ################################### # Constants for music stuff ################################### BASIC_NOTES = { "A": 440, "B": 493, "C": 261, "D": 293, "E": 329, "F": 349, "G": 392, } NOTES = {'f8': 5587, 'c#6': 1108, 'f4': 349, 'c7': 2093, 'd#2': 77, 'g8': 6271, 'd4': 293, 'd7': 2349, 'd#7': 2489, 'g#4': 415, 'e7': 2637, 'd9': 9397, 'b8': 7902, 'a#4': 466, 'b5': 987, 'b2': 123, 'g#9': 13289, 'g9': 12543, 'f#2': 92, 'c4': 261, 'e1': 41, 'e6': 1318, 'a#8': 7458, 'c5': 523, 'd6': 1174, 'd3': 146, 'g7': 3135, 'd2': 73, 'd#3': 155, 'g#6': 1661, 'd#4': 311, 'a3': 219, 'g2': 97, 'c#5': 554, 'd#9': 9956, 'a8': 7040, 'a#5': 932, 'd#5': 622, 'a1': 54, 'g#8': 6644, 'a2': 109, 'g#5': 830, 'f3': 174, 'a6': 1760, 'e8': 5274, 'c#9': 8869, 'f5': 698, 'b1': 61, 'c#4': 277, 'f#9': 11839, 'e5': 659, 'f9': 11175, 'f#5': 739, 'a#1': 58, 'f#8': 5919, 'b7': 3951, 'c#8': 4434, 'g1': 48, 'c#3': 138, 'f#7': 2959, 'c6': 1046, 'c#2': 69, 'c#7': 2217, 'c3': 130, 'e9': 10548, 'c9': 8372, 'a#6': 1864, 'a#7': 3729, 'g#2': 103, 'f6': 1396, 'b3': 246, 'g#3': 207, 'b4': 493, 'a7': 3520, 'd#6': 1244, 'd#8': 4978, 'f2': 87, 'd5': 587, 'f7': 2793, 'f#6': 1479, 'g6': 1567, 'e3': 164, 'f#3': 184, 'g#1': 51, 'd8': 4698, 'f#4': 369, 'f1': 43, 'c8': 4186, 'g4': 391, 'g3': 195, 'a4': 440, 'a#3': 233, 'd#1': 38, 'e2': 82, 'e4': 329, 'a5': 880, 'a#2': 116, 'g5': 783, 'g#7': 3322, 'b6': 1975, 'c2': 65, 'f#1': 46 } DURATIONS = {"BREVE": 2000, "SEMIBREVE": 1000, "MINIM": 500, "CROTCHET": 250, "QUAVER": 125, "SEMIQUAVER": 63, "DEMISEMIQUAVER": 32, "HEMIDEMISEMIQUAVER": 16 } ############################################### # USEFUL STATIC METHODS ############################################### @staticmethod def CENTER(win, up=0): gui.SET_LOCATION("CENTER", win=win, up=up) @staticmethod def SET_LOCATION(x, y=None, ignoreSettings=None, win=None, up=0): if ignoreSettings is not None: win.ignoreSettings = ignoreSettings if gui.GET_PLATFORM() != gui.LINUX: trans = win.attributes('-alpha') win.attributes('-alpha', 0.0) win.update_idletasks() if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre'] and y is None: x = y = 'c' else: x, y = gui.PARSE_TWO_PARAMS(x, y) gui.trace("Set location called with %s, %s", x, y) # get the window's dimensions dims = gui.GET_DIMS(win) # set any center positions if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre']: x = dims["x"] if isinstance(y, UNIVERSAL_STRING) and y.lower() in ['c', 'center', 'centre']: y = dims["y"] # move the window up a bit if requested y = y - up if up < y else 0 # fix any out of bounds positions if x < 0 or x > dims['s_width']: x = dims['x'] if y < 0 or y > dims['s_height']: y = dims['y'] gui.trace("Screen: %sx%s. Requested: %sx%s. Location: %s, %s", dims["s_width"], dims["s_height"], dims["b_width"], dims["b_height"], x, y) win.geometry("+%d+%d" % (x, y)) win.locationSet = True if gui.GET_PLATFORM() != gui.LINUX: win.attributes('-alpha', trans) @staticmethod def CLEAN_CONFIG_DICTIONARY(**kw): """ Used by all Classes to tidy up dictionaries passed into config functions Allows us to more quickly process the dictionaries when overriding config """ try: kw['bg'] = kw.pop('background') except: pass try: kw['fg'] = kw.pop('foreground') except: pass kw = dict((k.lower().strip(), v) for k, v in kw.items()) return kw @staticmethod def GET_PLATFORM(): """ returns one of the gui class's three static platform variables """ if platform() in ["win32", "Windows"]: return gui.WINDOWS elif platform() == "Darwin": return gui.MAC elif platform() in ["Linux", "FreeBSD"]: return gui.LINUX else: raise Exception("Unknown platform: " + platform()) @staticmethod def SHOW_VERSION(): """ returns a printable string containing version information """ verString = \ "appJar: " + str(__version__) \ + "\nPython: " + str(sys.version_info[0]) \ + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2]) \ + "\nTCL: " + str(TclVersion) \ + ", TK: " + str(TkVersion) \ + "\nPlatform: " + str(platform()) \ + "\npid: " + str(os.getpid()) \ + "\nlocale: " + str(__locale__) return verString @staticmethod def SHOW_PATHS(): """ returns a printable string containing path to libraries, etc """ pathString = \ "File Name: " + (gui.exe_file if gui.exe_file is not None else "") \ + "\nFile Location: " + (gui.exe_path if gui.exe_path is not None else "") \ + "\nLib Location: " + (gui.lib_path if gui.lib_path is not None else "") return pathString @staticmethod def GET_DIMS(container): """ returns a dictionary of dimensions for the supplied container """ container.update() dims = {} # get the apps requested width & height dims["r_width"] = container.winfo_reqwidth() dims["r_height"] = container.winfo_reqheight() # get the current width & height dims["w_width"] = container.winfo_width() dims["w_height"] = container.winfo_height() # get the window's width & height dims["s_width"] = container.winfo_screenwidth() dims["s_height"] = container.winfo_screenheight() # determine best geom for OS # on MAC & LINUX, w_width/w_height always 1 unless user-set # on WIN, w_height is bigger then r_height - leaving empty space if gui.GET_PLATFORM() in [gui.MAC, gui.LINUX]: if dims["w_width"] != 1: dims["b_width"] = dims["w_width"] dims["b_height"] = dims["w_height"] else: dims["b_width"] = dims["r_width"] dims["b_height"] = dims["r_height"] else: dims["b_height"] = max(dims["r_height"], dims["w_height"]) dims["b_width"] = max(dims["r_width"], dims["w_width"]) # GUI's corner - widget's corner # widget's corner can be 0 on windows when size not set by user dims["outerFrameWidth"] = 0 if container.winfo_x() == 0 else container.winfo_rootx() - container.winfo_x() dims["titleBarHeight"] = 0 if container.winfo_rooty() == 0 else container.winfo_rooty() - container.winfo_y() # add it all together dims["actualWidth"] = dims["b_width"] + (dims["outerFrameWidth"] * 2) dims["actualHeight"] = dims["b_height"] + dims["titleBarHeight"] + dims["outerFrameWidth"] dims["x"] = (dims["s_width"] // 2) - (dims["actualWidth"] // 2) dims["y"] = (dims["s_height"] // 2) - (dims["actualHeight"] // 2) return dims @staticmethod def PARSE_TWO_PARAMS(x, y): """ used to convert different possible x/y params to a tuple """ if y is not None: return (x,y) else: if isinstance(x, (list, tuple)): return (x[0], x[1]) else: if isinstance(x, UNIVERSAL_STRING): x=x.strip() if "," in x: return [int(w.strip()) for w in x.split(",")] return (x, x) @staticmethod def SPLIT_GEOM(geom): """ returns 2 lists made from the geom string :param geom: the geom string to parse :returns: a tuple containing a width/heiht tuple & a x/y position tuple """ geom = geom.lower().split("x") width = int(float(geom[0])) height = int(float(geom[1].split("+")[0])) try: x = int(float(geom[1].split("+")[1])) y = int(float(geom[1].split("+")[2])) except IndexError: x = y = -1 return (width, height), (x, y) @staticmethod def MOUSE_POS_IN_WIDGET(widget, event, findRoot=True): """ returns the mouse's relative position in a widget :param widget: the widget to look in :param event: the event containing the mouse coordinates :param findRoot: if we should make this relative to the parent """ # first we have to get the real master master = widget while findRoot: if isinstance(master, (SubWindow, Tk)): findRoot = False else: master = master.master # subtract the widget's top left corner from the root window's top corner x = event.x_root - master.winfo_rootx() y = event.y_root - master.winfo_rooty() gui.trace("<<MOUSE_POS_IN_WIDGET>> %s %s,%s", widget, x, y) return (x, y) ##################################### ##################################### # CONSTRUCTOR - creates the GUI ##################################### def __init__( self, title=None, geom=None, handleArgs=True, language=None, startWindow=None, useTtk=False, useSettings=False, showIcon=True, **kwargs ): """ constructor - sets up the empty GUI window, and inits the various properties """ if self.__class__.instantiated: raise Exception("You cannot have more than one instance of gui, try using a subWindow.") else: self.__class__.instantiated = True self.alive = True # first up, set the logger def _logForLevel(self, message, *args, **kwargs): if self.isEnabledFor(logging.DEBUG-5): self._log(logging.DEBUG-5, message, args, **kwargs) def _logToRoot(message, *args, **kwargs): logging.log(logging.DEBUG-5, message, *args, **kwargs) logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(name)s:%(levelname)s %(message)s') logging.addLevelName(logging.DEBUG - 5, 'TRACE') setattr(logging, 'TRACE', logging.DEBUG -5) setattr(logging.getLoggerClass(), "trace", _logForLevel) setattr(logging, "trace", _logToRoot) logFile = kwargs.pop("file", kwargs.pop("logFile", None)) logLevel = kwargs.pop("log", kwargs.pop("logLevel", None)) self._language = language self.useSettings = useSettings self.settingsFile = "appJar.ini" self.externalSettings = {} self.startWindow = startWindow # check any command line arguments if argparse is None: handleArgs = False args = self._handleArgs() if handleArgs else None # warn if we're in an untested mode self._checkMode() # first out, verify the platform self.platform = gui.GET_PLATFORM() # process any command line arguments self.ttkFlag = False selectedTtkTheme = None if handleArgs: if args.f: gui.setLogFile(args.f) logFile = None # don't use any param logFile tmplevel, logLevel = logLevel, None if args.c: gui.setLogLevel("CRITICAL") elif args.e: gui.setLogLevel("ERROR") elif args.w: gui.setLogLevel("WARNING") elif args.i: gui.setLogLevel("INFO") elif args.d: gui.setLogLevel("DEBUG") elif args.t: gui.setLogLevel("TRACE") else: loglevel = tmplevel if logFile is not None: gui.setLogFile(logFile) if logLevel is not None: gui.setLogLevel(logLevel) if handleArgs: if args.l: self._language = args.l if args.ttk: useTtk = True if args.ttk is not True: selectedTtkTheme = args.ttk if args.s: self.useSettings = True if args.s is not True: self.settingsFile = args.s # configure as ttk if useTtk: self._useTtk() if useTtk is not True: selectedTtkTheme = useTtk # a stack to hold containers as being built # done here, as initArrays is called elsewhere - to reset the gubbins self.containerStack = [] self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}} # first up, set up all the data stores self.widgetManager = WidgetManager() self.accessMade = False # accessibility subWindow self.splashConfig = None # splash screen? self.dnd = None # the dnd manager self.doFlash = False # set up flash variable self.hasTitleBar = True # used to hide/show title bar # validate function callbacks - used by numeric texts # created first time a widget is used self.validateNumeric = None self.validateSpinBox = None # dynamically create lots of functions for configuring stuff self._buildConfigFuncs() # language parser self.configParser = None # set up some default path locations # this fails if in interactive mode.... try: gui.exe_file = str(os.path.basename(theMain.__file__)) gui.exe_path = str(os.path.dirname(theMain.__file__)) except: pass gui.lib_file = os.path.abspath(__file__) gui.lib_path = os.path.dirname(gui.lib_file) # location of appJar self.resource_path = os.path.join(gui.lib_path, "resources") self.icon_path = os.path.join(self.resource_path, "icons") self.sound_path = os.path.join(self.resource_path, "sounds") self.appJarIcon = os.path.join(self.icon_path, "favicon.ico") # user configurable self.userImages = gui.exe_path self.userSounds = gui.exe_path # create the main window - topLevel self.topLevel = Tk() self.topLevel.bind('<Configure>', self._windowEvent) def _setFocus(e): try: e.widget.focus_set() except: pass # these are specifically to make right-click menus disapear on linux self.topLevel.bind('<Button-1>', lambda e: _setFocus(e)) self.topLevel.bind('<Button-2>', lambda e: _setFocus(e)) self.topLevel.bind('<Button-3>', lambda e: _setFocus(e)) # override close button self.topLevel.protocol("WM_DELETE_WINDOW", self.stop) # temporarily hide it self.topLevel.withdraw() # used to keep a handle on the last pop-up dialog # allows the dialog to be closed remotely # mainly for test-automation self.topLevel.POP_UP = None # create a frame to store all the widgets #Â now a canvas to allow animation... self.appWindow = CanvasDnd(self.topLevel) self.appWindow.pack(fill=BOTH, expand=True) self.topLevel.canvasPane = self.appWindow # set the windows title if title is None: title = "appJar" if gui.exe_file is None else gui.exe_file self.setTitle(title) self.topLevel.winIcon = None # will store the path to any icon # configure the geometry of the window self.topLevel.escapeBindId = None # used to exit fullscreen self.topLevel.stopFunction = None # used to exit fullscreen self.topLevel.startFunction = None # set the resize status - default to True self.topLevel.locationSet = False self.topLevel.ignoreSettings = False self.topLevel.isFullscreen = False # records if we're in fullscreen - stops hideTitle from breaking self.topLevel.displayed = True if geom is not None: self.setSize(geom) self.setResizable(True) self.Widgets = WIDGET_NAMES # 3 fonts used for most widgets self._buttonFont = tkFont.Font(family="Helvetica", size=12,) self._labelFont = tkFont.Font(family="Helvetica", size=12) self._inputFont = tkFont.Font(family="Helvetica", size=12) self._statusFont = tkFont.Font(family="Helvetica", size=12) # dedicated font for access widget self._accessFont = tkFont.Font(family="Arial", size=11,) # dedicated font for links - forces bold & underlined, but updated with label fonts self._linkFont = tkFont.Font(family="Helvetica", size=12, weight='bold', underline=1) self.tableFont = tkFont.Font(family="Helvetica", size=12) # create a menu bar - only shows if populated # now created in menu functions, as it generated a blank line... self.hasMenu = False self.hasStatus = False self.copyAndPaste = CopyAndPaste(self.topLevel, self) class Toolbar(frameBase, object): def __init__(self, master, **kwargs): super(Toolbar, self).__init__(master, **kwargs) self.BG_COLOR = None self.pinned = True self.pinBut = None self.inUse = False self.toolbarMin = None self.location = None def makeMinBar(self): if self.toolbarMin is None: self.toolbarMin = Frame(self.master, bd=1, relief=RAISED) self.toolbarMin.config(bg="gray", height=3) self.bind("<Leave>", self._minToolbar) self.toolbarMin.bind("<Enter>", self._maxToolbar) def hide(self): if self.inUse: self.pack_forget() if self.toolbarMin is not None: self.toolbarMin.pack_forget() def show(self): if self.inUse: self.pack(before=self.location, side=TOP, fill=X) if self.toolbarMin is not None: self.toolbarMin.pack_forget() def _minToolbar(self, e=None): if not self.pinned: if self.toolbarMin is not None: self.toolbarMin.config(width=self.winfo_reqwidth()) self.toolbarMin.pack(before=self.location, side=TOP, fill=X) self.pack_forget() def _maxToolbar(self, e=None): self.pack(before=self.location, side=TOP, fill=X) if self.toolbarMin is not None: self.toolbarMin.pack_forget() class WidgetContainer(frameBase, object): def __init__(self, master, **kwargs): super(WidgetContainer, self).__init__(master, **kwargs) # create the main container for this GUI container = WidgetContainer(self.appWindow) # container = Label(self.appWindow) # made as a label, so we can set an # image if not self.ttkFlag: container.config(padx=2, pady=2, background=self.topLevel.cget("bg")) container.pack(fill=BOTH, expand=True) self._addContainer("root", WIDGET_NAMES.RootPage, container, 0, 1) self.tb = Toolbar(self.appWindow) if not self.ttkFlag: self.tb.config(bd=1, relief=RAISED) else: self.tb.config(style="Toolbar.TFrame") # set up the main container to be able to host an image self._configBg(container) if self.platform == self.WINDOWS and showIcon: try: self.setIcon(self.appJarIcon) except: # file not found gui.trace("Error setting Windows default icon") # set the ttk theme if self.ttkFlag: self.setTtkTheme(selectedTtkTheme) # for configuting event processing self.EVENT_SIZE = 1000 self.EVENT_SPEED = 100 self.preloadAnimatedImageId = None self.processQueueId = None # an array to hold any threaded events.... self.events = [] self.pollTime = 250 self._fastStop = False self.configure(**kwargs) # special bindings self._globalBindings() self.built = True def _globalBindings(self): def _selectEntry(event): event.widget.select_range(0, 'end') def _selectText(event): event.widget.tag_add("sel","1.0","end") def _scrollPaste(event): event.widget.event_generate('<<Paste>>') event.widget.see(END) if self.GET_PLATFORM() == self.MAC: self.topLevel.bind_class("Text", "<Command-a>", _selectText) self.topLevel.bind_class("Entry", "<Command-a>", _selectEntry) self.topLevel.bind_class("Text", "<Command-v>", _scrollPaste) else: self.topLevel.bind_class("Text", "<Control-a>", _selectText) self.topLevel.bind_class("Entry", "<Control-a>", _selectEntry) self.topLevel.bind_class("Text", "<Control-v>", _scrollPaste) def _handleArgs(self): """ internal function to handle command line arguments """ parser = argparse.ArgumentParser( description="appJar - the easiest way to create GUIs in python", epilog="For more information, go to: http://appJar.info" ) parser.add_argument("-v", "--version", action="version", version=gui.SHOW_VERSION(), help="show version information and exit") logGroup = parser.add_mutually_exclusive_group() logGroup.add_argument("-c", action="store_const", const=True, help="only log CRITICAL messages") logGroup.add_argument("-e", action="store_const", const=True, help="log ERROR messages and above") logGroup.add_argument("-w", action="store_const", const=True, help="log WARNING messages and above") logGroup.add_argument("-i", action="store_const", const=True, help="log INFO messages and above") logGroup.add_argument("-d", action="store_const", const=True, help="log DEBUG messages and above") logGroup.add_argument("-t", action="store_const", const=True, help="log TRACE messages and above") parser.add_argument("-l", metavar="LANGUAGE.ini", help="set a language file to use") parser.add_argument("-f", metavar="file.log", help="set a log file to use") parser.add_argument("-s", metavar="SETTINGS", const=True, nargs="?", help="load settings, from an optional file name") parser.add_argument("--ttk", metavar="THEME", const=True, nargs="?", help="enable ttk, with an optional theme") return parser.parse_args() # function to check on mode def _checkMode(self): """ internal function to warn about issues in certain modes """ # detect if we're in interactive mode if hasattr(sys, 'ps1'): self.warn("Interactive mode is not fully tested, some features might not work.") else: if sys.flags.interactive: self.warn("Postmortem Interactive mode is not fully tested, some features might not work.") # also, check for iPython try: __IPYTHON__ except NameError: #no iPython - ignore pass else: self.warn("iPython is not fully tested, some features might not work.") def _configBg(self, container): """ internal function to set up a label as the BG """ # set up a background image holder # alternative to label option above, as label doesn't update widgets # properly class BgLabel(labelBase, object): def __init__(self, master, **kwargs): super(BgLabel, self).__init__(master, **kwargs) if not self.ttkFlag: self.bgLabel = BgLabel(container, anchor=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) else: self.bgLabel = ttk.Label(container) self.bgLabel.place(x=0, y=0, relwidth=1, relheight=1) container.image = None ##################################### # TTK functions ##################################### def _useTtk(self): """ enables use of ttk """ global ttk, frameBase, labelBase, scaleBase, entryBase try: import ttk except: try: from tkinter import ttk except: gui.error("ttk not available") return self.ttkFlag = True frameBase = ttk.Frame labelBase = ttk.Label scaleBase = ttk.Scale entryBase = ttk.Entry gui.trace("Mode switched to ttk") def _loadTtkThemes(self): global ThemedStyle if ThemedStyle is None: try: from ttkthemes import ThemedStyle self.ttkStyle = ThemedStyle(self.topLevel) except: ThemedStyle = False def getTtkThemes(self, loadThemes=False): if loadThemes: self._loadTtkThemes() if not ThemedStyle: self.error("Custom ttkThemes not available") return self.ttkStyle.theme_names() def getTtkTheme(self): return self.ttkStyle.theme_use() # only call this after the main tk has been created # otherwise we get two windows! def setTtkTheme(self, theme=None): """ sets the ttk theme to use """ self.ttkStyle = ttk.Style() gui.trace("Switching ttk theme to: %s", theme) if theme is not None: try: self.ttkStyle.theme_use(theme) except: gui.trace("no basic ttk theme named %s found, searching for additional themes", theme) self._loadTtkThemes() if not ThemedStyle: self.error("ttk theme: %s unavailable. Try one of: %s", theme, str(self.ttkStyle.theme_names())) else: self.ttkStyle.set_theme(theme) # set up our ttk styles self.ttkStyle.configure("DefaultText.TEntry", foreground="grey") self.ttkStyle.configure("ValidationEntryValid.TEntry", foreground="#4CC417", highlightbackground="#4CC417", highlightcolor="#4CC417", highlightthickness='20') self.ttkStyle.configure("ValidationEntryInvalid.TEntry", foreground="#FF0000", highlightbackground="#FF0000", highlightcolor="#FF0000", highlightthickness='20') self.ttkStyle.configure("ValidationEntryWait.TEntry", foreground="#000000", highlightbackground="#000000", highlightcolor="#000000", highlightthickness='20') self.ttkStyle.configure("ValidationEntryValid.TLabel", foreground="#4CC417") self.ttkStyle.configure("ValidationEntryInvalid.TLabel", foreground="#FF0000") self.ttkStyle.configure("ValidationEntryWait.TLabel", foreground="#000000") self.ttkStyle.configure("Link.TLabel", foreground="#0000ff") self.ttkStyle.configure("LinkOver.TLabel", foreground="#3366ff") #toolbars self.ttkStyle.configure("Toolbar.TFrame") self.ttkStyle.configure("Toolbar.TLabel") self.ttkStyle.configure("Toolbar.TButton", compound=CENTER, padding=0, expand=0) # self.fgColour = self.topLevel.cget("foreground") # self.buttonFgColour = self.topLevel.cget("foreground") # self.labelFgColour = self.topLevel.cget("foreground") # set a property for ttk theme ttkTheme = property(getTtkTheme, setTtkTheme) ############################################################### # library loaders - on demand loading of different classes ############################################################### def _loadRandom(self): """ loasd random libraries """ global random if random is None: import random def _loadTurtle(self): """ loasd turtle libraries """ global turtle try: import turtle except: turtle = False self.error("Turtle not available") def _loadConfigParser(self): """ loads the ConfigParser, used by internationalisation & settings """ global ConfigParser, ParsingError, codecs if ConfigParser is None: try: from configparser import ConfigParser from configparser import ParsingError import codecs except: try: from ConfigParser import ConfigParser from ConfigParser import ParsingError import codecs except: ConfigParser = ParsingError = codecs = False self.configParser = None return self.configParser = ConfigParser() self.configParser.optionxform = str def _loadHashlib(self): """ loads hashlib - used by text area """ global hashlib if hashlib is None: try: import hashlib except: hashlib = False def _loadTooltip(self): """ loads tooltips - used all over """ global ToolTip if ToolTip is None: try: from appJar.lib.tooltip import ToolTip except: ToolTip = False def _loadMatplotlib(self): """ loads matPlotLib """ global PlotCanvas, PlotNav, PlotFig if PlotCanvas is None: try: from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg as PlotCanvas try: from matplotlib.backends.backend_tkagg import NavigationToolbar2Tk as PlotNav except: from matplotlib.backends.backend_tkagg import NavigationToolbar2TkAgg as PlotNav from matplotlib.figure import Figure as PlotFig except: PlotCanvas = PlotNav = PlotFig = False def _loadExternalDnd(self): """ loads external dnd - from other applications """ global EXTERNAL_DND if EXTERNAL_DND is None: try: tkdndlib = os.path.join(os.path.dirname(os.path.abspath(__file__)), "lib", "tkdnd2.8") os.environ['TKDND_LIBRARY'] = tkdndlib from appJar.lib.TkDND_wrapper import TkDND as EXTERNAL_DND self.dnd = EXTERNAL_DND(self.topLevel) except: EXTERNAL_DND = False def _loadInternalDnd(self): """ loads the internal dnd libraries """ global INTERNAL_DND, types if INTERNAL_DND is None: try: import Tkdnd as INTERNAL_DND import types as types except: try: from tkinter import dnd as INTERNAL_DND import types as types except: INTERNAL_DND = False types = False def _loadURL(self): """ loads ibraries used by googlemaps widget """ global base64, urlencode, urlopen, urlretrieve, quote_plus, json, Queue self._loadThreading() if Queue: if urlencode is None: try: # python 2 from urllib import urlencode, urlopen, urlretrieve, quote_plus import json import base64 except ImportError: # python 3 try: from urllib.parse import urlencode from urllib.parse import quote_plus from urllib.request import urlopen from urllib.request import urlretrieve import json import base64 except: base64 = urlencode = urlopen = urlretrieve = quote_plus = json = Queue = False else: base64 = urlencode = urlopen = urlretrieve = quote_plus = json = Queue = False def _loadThreading(self): """ loads threading classes, and sets up queue """ global Thread, Queue if Thread is None: try: from threading import Thread import Queue except ImportError: # python 3 try: from threading import Thread import queue as Queue except: Thread = Queue = False return self.eventQueue = Queue.Queue(maxsize=self.EVENT_SIZE) self._processEventQueue() def _loadNanojpeg(self): """ loads jpeg support """ global nanojpeg, array if nanojpeg is None: try: from appJar.lib import nanojpeg import array except: nanojpeg = False array = False def _loadWinsound(self): """ loads winsound support on Windows """ global winsound if winsound is None: if platform() in ["win32", "Windows"]: import winsound else: winsound = False def _importPngimagetk(self): """ loads PNG support """ global PngImageTk if PngImageTk is None: try: from appJar.lib.tkinter_png import PngImageTk except: PngImageTk = False def _importAjtree(self): """ loads tree support - and creates tree classes """ global parseString, TreeItem, TreeNode if TreeNode is None: try: from idlelib.TreeWidget import TreeItem, TreeNode except: try: from idlelib.tree import TreeItem, TreeNode except: gui.warning("no trees") TreeItem = TreeNode = parseString = False if TreeNode is not False: try: from xml.dom.minidom import parseString except: gui.warning("no parse string") TreeItem = TreeNode = parseString = False return def _importSqlite3(self): """ loads sqlite3 """ global sqlite3 if sqlite3 is None: try: import sqlite3 except: sqlite3 = False def _importWebBrowser(self): """ loads webbrowser """ global webbrowser if webbrowser is None: try: import webbrowser except: webbrowser = False ##################################### # FUNCTIONS FOR UNIVERSAL DND ##################################### def _registerExternalDragSource(self, title, widget, function=None): """ register a widget to start external drag events """ self._loadExternalDnd() if EXTERNAL_DND is not False: try: self.dnd.bindsource(widget, self._startExternalDrag, 'text/uri-list') self.dnd.bindsource(widget, self._startExternalDrag, 'text/plain') widget.dndFunction = function widget.dragData = None except: # dnd not working on this platform raise Exception("Failed to register external Drag'n Drop for: " + str(title)) else: raise Exception("External Drag'n Drop not available on this platform") def _registerExternalDropTarget(self, title, widget, function=None, replace=True): """ register a widget to receive external drag events """ self._loadExternalDnd() if EXTERNAL_DND is not False: try: self.dnd.bindtarget(widget, self._receiveExternalDrop, 'text/uri-list') self.dnd.bindtarget(widget, self._receiveExternalDrop, 'text/plain') # cater for new drop parameter in new setters if function == True: function = None widget.dndFunction = function widget.dropData = None widget.dropReplace = replace except: # dnd not working on this platform raise Exception("Failed to register external Drag'n Drop for: " + str(title)) else: raise Exception("External Drag'n Drop not available on this platform") def _registerInternalDragSource(self, kind, title, widget, function=None): """ register a widget to start internal drag events """ self._loadInternalDnd() name = None if kind == WIDGET_NAMES.Label: name = self.getLabel(title) if INTERNAL_DND is not False: try: widget.bind('<ButtonPress>', lambda e: self._startInternalDrag(e, title, name, widget)) widget.dnd_canvas = self._getCanvas().canvasPane gui.trace("DND drag source created: %s on canvas %s", widget, widget.dnd_canvas) except: raise Exception("Failed to register internal Drag'n Drop for: " + str(title)) else: raise Exception("Internal Drag'n Drop not available on this platform") def _registerInternalDropTarget(self, widget, function): """ register a widget to receive internal drag events """ gui.trace("<<WIDGET._registerInternalDropTarget>> %s", widget) self._loadInternalDnd() if not INTERNAL_DND: raise Exception("Internal Drag'n Drop not available on this platform") # called by DND class, when looking for a DND target def dnd_accept(self, source, event): gui.trace("<<WIDGET.dnd_accept>> %s - %s", widget, self.dnd_canvas) return self # This is called when the mouse pointer goes from outside the # Target Widget to inside the Target Widget. def dnd_enter(self, source, event): gui.trace("<<WIDGET.dnd_enter>> %s", widget) XY = gui.MOUSE_POS_IN_WIDGET(self,event) source.appear(self, XY) # This is called when the mouse pointer goes from inside the # Target Widget to outside the Target Widget. def dnd_leave(self, source, event): gui.trace("<<WIDGET.dnd_leave>> %s", widget) # hide the dragged object source.vanish() #This is called if the DraggableWidget is being dropped on us. def dnd_commit(self, source, event): source.vanish(all=True) gui.trace("<<WIDGET.dnd_commit>> %s Object received=%s", widget, source) #This is called when the mouse pointer moves within the TargetWidget. def dnd_motion(self, source, event): gui.trace("<<WIDGET.dnd_motion>> %s", widget) XY = gui.MOUSE_POS_IN_WIDGET(self,event) # move the dragged object source.move(self, XY) def keepWidget(self, title, name): if self.drop_function is not None: return self.drop_function(title, name) else: self.configParser(text=name) return True widget.dnd_accept = types.MethodType(dnd_accept, widget) widget.dnd_enter = types.MethodType(dnd_enter, widget) widget.dnd_leave = types.MethodType(dnd_leave, widget) widget.dnd_commit = types.MethodType(dnd_commit, widget) widget.dnd_motion = types.MethodType(dnd_motion, widget) widget.keepWidget = types.MethodType(keepWidget, widget) # save the underlying canvas widget.dnd_canvas = self._getCanvas().canvasPane widget.drop_function = function gui.trace("DND target created: %s on canvas %s", widget, widget.dnd_canvas) def _startInternalDrag(self, event, title, name, widget): """ called when the user initiates an internal drag event """ gui.trace("Internal drag started for %s on %s", title, widget) x, y = gui.MOUSE_POS_IN_WIDGET(widget, event, False) width = x / widget.winfo_width() height = y / widget.winfo_height() thingToDrag = DraggableWidget(widget.dnd_canvas, title, name, (width, height)) INTERNAL_DND.dnd_start(thingToDrag, event) def _startExternalDrag(self, event): """ starts external drags - not yet supported """ widgType = gui.GET_WIDGET_CLASS(event.widget) self.warn("Unable to initiate drag events: %s", widgType) def _receiveExternalDrop(self, event): """ receives external drag events """ widgType = gui.GET_WIDGET_CLASS(event.widget) event.widget.dropData = event.data if not hasattr(event.widget, 'dndFunction'): self.warn("Error - external drop target not correctly configured: %s", widgType) elif event.widget.dndFunction is not None: event.widget.dndFunction(event.data) else: if widgType in ["Entry", "AutoCompleteEntry", "SelectableLabel"]: if widgType == "SelectableLabel": event.widget.configure(state="normal") if event.widget.dropReplace: event.widget.delete(0, END) event.widget.insert(END, event.data) event.widget.focus_set() event.widget.icursor(END) if widgType == "SelectableLabel": event.widget.configure(state="readonly") elif widgType in ["TextArea", "AjText", "ScrolledText", "AjScrolledText"]: if event.widget.dropReplace: event.widget.delete(1.0, END) event.widget.insert(END, event.data) event.widget.focus_set() event.widget.see(END) elif widgType in ["Label"]: for k, v in self.widgetManager.group(WIDGET_NAMES.Image).items(): if v == event.widget: try: imgTemp = self.userImages image = self._getImage(event.data, False) self._populateImage(k, image) self.userImages = imgTemp except: self.errorBox("Error loading image", "Unable to load image: " + str(event.data)) return for k, v in self.widgetManager.group(WIDGET_NAMES.Label).items(): if v == event.widget: self.setLabel(k, event.data) return elif widgType in ["Listbox"]: for k, v in self.widgetManager.group(WIDGET_NAMES.ListBox).items(): if v == event.widget: self.addListItem(k, event.data) return elif widgType in ["Message"]: for k, v in self.widgetManager.group(WIDGET_NAMES.Message).items(): if v == event.widget: self.setMessage(k, event.data) return else: self.warn("Unable to receive drop events: %s", widgType) ##################################### # Language/Translation functions ##################################### def translate(self, key, default=None): """ returns a translated version of the key, using the current language if none found, returns the default value """ return self._translate(key, "EXTERNAL", default) def _translateSound(self, key): """ internal wrapper to translate sounds """ return self._translate(key, "SOUND", key) def _translatePopup(self, key, value): """ internal wrapper to translate popups """ pop = self._translate(key, "POPUP") if pop is None: return (key, value) else: return (pop[0], pop[1]) def _translate(self, key, section, default=None): """ returns the relevant key from the relevant section in the internally held translation dictionary - prepopulated when language was set """ if key in self.translations[section]: return self.translations[section][key] else: return default def getLanguage(self): ''' returns the current language ''' return self._language def setLanguage(self, language): """ wrapper for changeLanguage() """ self.changeLanguage(language) # function to update languages def changeLanguage(self, language): """ changes the language used by the GUI will iterate through all widgets and update their text as well as populate a translation dictionary for later lookups """ self._loadConfigParser() if not ConfigParser: self.error("Internationalisation not supported") return fileName = language.upper() + ".ini" gui.trace("Loading language file: %s", fileName) if not PYTHON2: try: with codecs.open(fileName, "r", "utf8") as langFile: self.configParser.read_file(langFile) except FileNotFoundError: self.error("Invalid language, file not found: %s", fileName) return else: try: try: with codecs.open(fileName, "r", "utf8") as langFile: self.configParser.read_file(langFile) except AttributeError: with codecs.open(fileName, "r", "utf8") as langFile: self.configParser.readfp(langFile) except IOError: self.error("Invalid language, file not found: %s", fileName) return except ParsingError: self.error("Translation failed - language file contains errors, ensure there is no whitespace at the beginning of any lines.") return gui.trace("Switching to: %s", language) self._language = language self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}} # loop through each section, get the relative set of widgets # change the text for section in self.configParser.sections(): getWidgets = True section = section.upper() gui.trace("\tSection: %s", section) # convert the section title to its code if section == "CONFIG": # skip the config section (for now) gui.trace("\tSkipping CONFIG") continue elif section == "TITLE": kind = WIDGET_NAMES.SubWindow elif section.startswith("TOOLTIP-"): kind = "TOOLTIP" getWidgets = False elif section in ["SOUND", "EXTERNAL", "POPUP"]: for (key, val) in self.configParser.items(section): if section == "POPUP": val = val.strip().split("\n") self.translations[section][key] = val gui.trace("\t\t%s: %s", key, val) continue elif section == "MENUBAR": for (key, val) in self.configParser.items(section): key = key.strip().split("-") gui.trace("\t\t%s: %s", key, val) if len(key) == 1: try: self.renameMenu(key[0], val) except: self.warn("Invalid key") elif len(key) == 2: try: self.renameMenuItem(key[0], key[1], val) except: self.warn("Invalid key") continue else: try: kind = WIDGET_NAMES.getIgnoreCase(section) except Exception: self.warn("Invalid config section: %s", section) continue # if necessary, use the code to get the widget list if getWidgets: widgets = self.widgetManager.group(kind) if kind in [WIDGET_NAMES.Scale]: self.warn("No text is displayed in %s. Maybe it has a Label?", section) continue elif kind in [WIDGET_NAMES.TextArea, WIDGET_NAMES.Meter, WIDGET_NAMES.PieChart, WIDGET_NAMES.Tree]: self.warn("No text is displayed in %s", section) continue elif kind in [WIDGET_NAMES.name(WIDGET_NAMES.SubWindow)]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) if key.lower() == "appjar": self.setTitle(val) elif key.lower() == "splash": if self.splashConfig is not None: gui.trace("\t\t Updated SPLASH to: %s", val) self.splashConfig['text'] = val else: gui.trace("\t\t No SPLASH to update") elif key.lower() == "statusbar": gui.trace("\tSetting STATUSBAR: %s", val) self.setStatusbarHeader(val) else: try: widgets[key].title(val) except KeyError: self.warn("Invalid SUBWINDOW: %s", key) elif kind in [WIDGET_NAMES.ListBox]: for k in widgets.keys(): lb = widgets[k] # convert data to a list if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = lb.DEFAULT_TEXT data = data.strip().split("\n") # tidy up the list data = [item.strip() for item in data if len(item.strip()) > 0] self.updateListBox(k, data) elif kind in [WIDGET_NAMES.SpinBox]: for k in widgets.keys(): sb = widgets[k] # convert data to a list if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = sb.DEFAULT_TEXT data = data.strip().split("\n") # tidy up the list data = [item.strip() for item in data if len(item.strip()) > 0] self.changeSpinBox(k, data) elif kind in [WIDGET_NAMES.OptionBox]: for k in widgets.keys(): ob = widgets[k] # convert data to a list if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = ob.DEFAULT_TEXT data = data.strip().split("\n") # tidy up the list data = [item.strip() for item in data if len(item.strip()) > 0] self.changeOptionBox(k, data) elif kind in [WIDGET_NAMES.RadioButton]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid RADIOBUTTON key: %s", key) else: try: rbs = self.widgetManager.get(WIDGET_NAMES.RadioButton, keys[0]) except KeyError: self.warn("Invalid RADIOBUTTON key: %s", keys[0]) continue for rb in rbs: if rb.DEFAULT_TEXT == keys[1]: rb["text"] = val break elif kind in [WIDGET_NAMES.TabbedFrame]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid TABBEDFRAME key: %s", key) else: try: self.setTabText(keys[0], keys[1], val) except ItemLookupError: self.warn("Invalid TABBEDFRAME: %s with TAB: %s" , keys[0], keys[1]) elif kind in [WIDGET_NAMES.Properties]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid PROPERTIES key: %s", key) else: try: self.setPropertyText(keys[0], keys[1], val) except ItemLookupError: self.warn("Invalid PROPERTIES: %s", keys[0]) except KeyError: self.warn("Invalid PROPERTY: %s", keys[1]) elif kind == WIDGET_NAMES.Tree: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid GRID key: %s", key) else: if keys[1] not in ["actionHeading", "actionButton", "addButton"]: self.warn("Invalid GRID label: %s for GRID: %s", keys[1], keys[0]) else: try: self.confGrid(keys[0], keys[1], val) except ItemLookupError: self.warn("Invalid GRID: %s", keys[0]) elif kind == self.PAGEDWINDOW: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid PAGEDWINDOW key: %s", key) else: if keys[1] not in ["prevButton", "nextButton", "title"]: self.warn("Invalid PAGEDWINDOW label: %s for PAGEDWINDOW: %s", keys[1], keys[0]) else: try: widgets[keys[0]].config(**{keys[1]:val}) except KeyError: self.warn("Invalid PAGEDWINDOW: %s", keys[0]) elif kind == WIDGET_NAMES.Entry: for k in widgets.keys(): ent = widgets[k] if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = ent.DEFAULT_TEXT gui.trace("\t\t%s: %s", k, data) self.setEntryDefault(k, data) elif kind in [WIDGET_NAMES.Image]: for k in widgets.keys(): if self.configParser.has_option(section, k): data = str(self.configParser.get(section, k)) try: self.setImage(k, data) gui.trace("\t\t%s: %s", k, data) except: self.error("Failed to update image: %s to: %s", k, data) else: gui.trace("No translation for: %s", k) elif kind in [WIDGET_NAMES.Label, WIDGET_NAMES.Button, WIDGET_NAMES.CheckBox, WIDGET_NAMES.Message, WIDGET_NAMES.Link, WIDGET_NAMES.LabelFrame, self.TOGGLEFRAME]: for k in widgets.keys(): widg = widgets[k] # skip validation labels - we don't need to translate them try: if kind == WIDGET_NAMES.Label and widg.isValidation: gui.trace("\t\t%s: skipping, validation label", k) continue except: pass if self.configParser.has_option(section, k): data = str(self.configParser.get(section, k)) else: data = widg.DEFAULT_TEXT gui.trace("\t\t%s: %s", k, data) widg.config(text=data) elif kind == WIDGET_NAMES.Toolbar: for k in widgets.keys(): but = widgets[k] if but.image is None: if self.configParser.has_option(section, k): data = str(self.configParser.get(section, k)) else: data = but.DEFAULT_TEXT gui.trace("\t\t%s: %s", k, data) but.config(text = data) elif kind == "TOOLTIP": try: kind = WIDGET_NAMES.name(WIDGET_NAMES.getIgnoreCase(section.split("-")[1])) func = getattr(self, "set"+kind+"Tooltip") except KeyError: self.warn("Invalid config section: TOOLTIP-%s", section) return gui.trace("Parsing TOOLTIPs for: %s", kind) for (key, val) in self.configParser.items(section): try: func(key, val) except ItemLookupError: self.warn("Invalid TOOLTIP for: %s, with key: %s", kind, key) continue else: self.warn("Unsupported widget: %s", section) continue language = property(getLanguage, changeLanguage) def showSplash(self, text="appJar", fill="#FF0000", stripe="#000000", fg="#FFFFFF", font=44): """ creates a splash screen to show at start up """ self.splashConfig= {'text':text, 'fill':fill, 'stripe':stripe, 'fg':fg, 'font':font} ################################################## ### Stuff for logging ################################################## @staticmethod def setLogFile(fileName): """ sets the filename for logging messages """ # Remove all handlers associated with the root logger object. for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) logging.basicConfig(level=logging.INFO, filename=fileName, format='%(asctime)s %(name)s:%(levelname)s: %(message)s') gui.info("Switched to logFile: %s", fileName) def _setLogFile(self, fileName): ''' necessary so we can access this as a property ''' gui.setLogFile(fileName) def getLogFile(self): return logging.root.handlers[0].baseFilename logFile = property(getLogFile, _setLogFile) @staticmethod def setLogLevel(level): """ main function for setting the logging level provide one of: INFO, DEBUG, WARNING, ERROR, CRITICAL, EXCEPTION, None """ logging.getLogger("appJar").setLevel(getattr(logging, level.upper())) gui.info("Log level changed to: %s", level) def getLogLevel(self): return logging.getLevelName(logging.getLogger("appJar").getEffectiveLevel()) def _setLogLevel(self, level): ''' necessary so we can access this as a property ''' gui.setLogLevel(level) logLevel = property(getLogLevel, _setLogLevel) @staticmethod def exception(message, *args): """ wrapper for logMessage - setting level to EXCEPTION """ gui.logMessage(message, "EXCEPTION", *args) @staticmethod def critical(message, *args): """ wrapper for logMessage - setting level to CRITICAL """ gui.logMessage(message, "CRITICAL", *args) @staticmethod def error(message, *args): """ wrapper for logMessage - setting level to ERROR """ gui.logMessage(message, "ERROR", *args) @staticmethod def warn(message, *args): """ wrapper for logMessage - setting level to WARNING """ gui.logMessage(message, "WARNING", *args) @staticmethod def debug(message, *args): """ wrapper for logMessage - setting level to DEBUG """ gui.logMessage(message, "DEBUG", *args) @staticmethod def trace(message, *args): """ wrapper for logMessage - setting level to TRACE """ gui.logMessage(message, "TRACE", *args) @staticmethod def info(message, *args): """ wrapper for logMessage - setting level to INFO """ gui.logMessage(message, "INFO", *args) @staticmethod def logMessage(msg, level, *args): """ allows user to log a message - provide a message and a log level any %s tags in the message will be replaced by the relevant positional *args """ frames = inspect.stack() # try to ensure we only log extras if we're called from above functions if frames[1][3] in ("exception", "critical", "error", "warn", "debug", "trace", "info"): callFrame = "" try: progName = gui.exe_file for s in frames: if progName in s[1]: callFrame = s break except: pass if callFrame != "": callFrame = "Line " + str(callFrame[2]) # user generated call if "appjar.py" not in frames[2][1] or frames[2][3] == "handlerFunction": if callFrame != "": msg = "[" + callFrame + "]: "+str(msg) # appJar logging else: if callFrame != "": msg = "["+callFrame + "->" + str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg) else: msg = "["+str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg) logger = logging.getLogger("appJar") level = level.upper() if level == "EXCEPTION": logger.exception(msg, *args) elif level == "CRITICAL": logger.critical(msg, *args) elif level == "ERROR": logger.error(msg, *args) elif level == "WARNING": logger.warning(msg, *args) elif level == "INFO": logger.info(msg, *args) elif level == "DEBUG": logger.debug(msg, *args) elif level == "TRACE": logger.trace(msg, *args) ############################################################## # Event Loop - must always be called at end ############################################################## def __enter__(self): """ allows gui to be used as a ContextManager """ gui.trace("ContextManager: initialised") return self def __exit__(self, eType, eValue, eTrace): """ allows gui to be used as a ContextManager - calls the go() function """ if eType is not None: self.error("ContextManager failed: %s", eValue) return False else: gui.trace("ContextManager: starting") self.go(startWindow=self.startWindow) return True def go(self, language=None, startWindow=None): """ Most important function! starts the GUI """ # check if we have a command line language if self._language is not None: language = self._language # if language is populated, we are in internationalisation mode # call the changeLanguage function - to re-badge all the widgets if language is not None: self.changeLanguage(language) if self.splashConfig is not None: gui.trace("SPLASH: %s", self.splashConfig) splash = SplashScreen( self.topLevel, self.splashConfig['text'], self.splashConfig['fill'], self.splashConfig['stripe'], self.splashConfig['fg'], self.splashConfig['font'] ) self.topLevel.withdraw() self._bringToFront(splash) # check the containers have all been stopped if len(self.containerStack) > 1: for i in range(len(self.containerStack) - 1, 0, -1): kind = self.containerStack[i]['type'] if kind != WIDGET_NAMES.Pane: self.warn("No stopContainer called on: %s", WIDGET_NAMES.name(kind)) # update any trees for k in self.widgetManager.group(WIDGET_NAMES.Tree): self.generateTree(k) # create appJar menu, if no menuBar created if not self.hasMenu: self.addAppJarMenu() if self.platform == self.WINDOWS: self.menuBar.add_cascade(menu=self.widgetManager.get(WIDGET_NAMES.Menu, "WIN_SYS")) self.topLevel.config(menu=self.menuBar) if startWindow is not None: self.startWindow = startWindow gui.trace("startWindow parameter: %s", startWindow) # pack it all in & make sure it's drawn self.appWindow.pack(fill=BOTH) if self.useSettings: self.loadSettings(self.settingsFile) self.topLevel.update_idletasks() # check geom is set and set a minimum size, also positions the window if necessary if not self.topLevel.locationSet: self.setLocation('CENTER') if not hasattr(self.topLevel, 'ms'): self.setMinSize() if self.splashConfig is not None: time.sleep(3) splash.destroy() # user hasn't specified anything if self.startWindow is None: if not self.topLevel.displayed: gui.trace("topLevel has been manually hidden - not showing in go()") else: gui.trace("Showing topLevel") self._bringToFront() self.topLevel.deiconify() else: gui.trace("hiding main window") self.hide() sw = self.widgetManager.get(WIDGET_NAMES.SubWindow, startWindow) if sw.blocking: raise Exception("Unable to start appjar with a blocking subWindow") self.showSubWindow(startWindow) # required to make the gui reopen after minimising if self.GET_PLATFORM() == self.MAC:self.topLevel.createcommand('tk::mac::ReopenApplication', self._macReveal) # start the call back & flash loops self._poll() self._flash() # register start-up function if self.topLevel.startFunction is not None: self.topLevel.after_idle(self.topLevel.startFunction) # start the main loop try: self.topLevel.mainloop() except(KeyboardInterrupt, SystemExit) as e: gui.trace("appJar stopped through ^c or exit()") self.stop() except Exception as e: self.exception(e) self.stop() def setStartFunction(self, func): f = self.MAKE_FUNC(func, "start") self.topLevel.startFunction = f startFunction = property(fset=setStartFunction) def _macReveal(self): """ internal function to deiconify GUIs on mac """ if self.topLevel.state() != "withdrawn": self.topLevel.deiconify() for k, v in self.widgetManager.group(WIDGET_NAMES.SubWindow).items(): if v.state() == "normal": self.showSubWindow(k) def setStopFunction(self, function): """ Set a function to call when the GUI is quit. Must return True or False """ tl = self._getTopLevel() tl.stopFunction = function # link to exit item in topMenu # only if in root if self._getContainerProperty('type') != WIDGET_NAMES.SubWindow: tl.createcommand('exit', self.stop) stopFunction = property(fset=setStopFunction) def setSetting(self, name, value): """ adds a setting to the settings file """ self.externalSettings[name] = value def getSetting(self, name, default=None): """ gets a setting form the settings file """ try: return self.externalSettings[name] except: return default def saveSettings(self, fileName="appJar.ini"): """ saves the current settings to a file called automatically by stop() of settings were loaded at start """ self._loadConfigParser() if not ConfigParser: self.error("Unable to save config file - no configparser") return settings = ConfigParser() settings.optionxform = str settings.add_section('GEOM') geom = self.topLevel.geometry() ms = self.topLevel.minsize() ms = "%s,%s" % (ms[0], ms[1]) settings.set('GEOM', 'geometry', geom) gui.trace("Save geom as: %s", geom) settings.set('GEOM', 'minsize', ms) settings.set('GEOM', "fullscreen", str(self.topLevel.attributes('-fullscreen'))) settings.set('GEOM', "state", str(self.topLevel.state())) # get toolbar setting if self.tb.inUse: gui.trace("Saving toolbar settings") settings.add_section("TOOLBAR") settings.set("TOOLBAR", "pinned", str(self.tb.pinned)) # get container settings for k, v in self.widgetManager.group(WIDGET_NAMES.ToggleFrame).items(): gui.trace("Saving toggle %s", k) if "TOGGLES" not in settings.sections(): settings.add_section("TOGGLES") settings.set("TOGGLES", k, str(v.isShowing())) for k, v in self.widgetManager.group(WIDGET_NAMES.TabbedFrame).items(): gui.trace("Saving tab %s", k) if "TABS" not in settings.sections(): settings.add_section("TABS") settings.set("TABS", k, str(v.getSelectedTab())) for k, v in self.widgetManager.group(WIDGET_NAMES.PagedWindow).items(): gui.trace("Saving page %s", k) if "PAGES" not in settings.sections(): settings.add_section("PAGES") settings.set("PAGES", k, str(v.getPageNumber())) for k, v in self.widgetManager.group(WIDGET_NAMES.SubWindow).items(): if "SUBWINDOWS" not in settings.sections(): settings.add_section("SUBWINDOWS") if v.shown: v.update() settings.set("SUBWINDOWS", k, "True") settings.add_section(k) settings.set(k, "geometry", v.geometry()) ms = v.minsize() settings.set(k, 'minsize', "%s,%s" % (ms[0], ms[1])) settings.set(k, "state", v.state()) gui.trace("Saving subWindow %s: geom=%s, state=%s, minsize=%s", k, v.geometry(), v.state(), ms) else: settings.set("SUBWINDOWS", k, "False") gui.trace("Skipping subwindow: %s", k) for k, v in self.externalSettings.items(): if "EXTERNAL" not in settings.sections(): settings.add_section("EXTERNAL") settings.set("EXTERNAL", k, str(v)) # pane positions? # sub windows geom & visibility # scrollpane x & y positions # language # ttk # debug level with open(fileName, 'w') as theFile: settings.write(theFile) def loadSettings(self, fileName="appJar.ini", useSettings=True): """ loads setting from a settings file, and adjusts the GUI to match called by go() function, if user has requested settings """ self._loadConfigParser() if not ConfigParser: self.error("Unable to save config file - no configparser") return self.useSettings = useSettings settings = ConfigParser() settings.optionxform = str settings.read(fileName) if settings.has_option("GEOM", "geometry"): geom = settings.get("GEOM", "geometry") if not self.topLevel.ignoreSettings: size, loc = gui.SPLIT_GEOM(geom) gui.trace("Setting topLevel geom: %s as size: %s, loc: %s", geom, size, loc) if size[0] > 1: self.setSize(*size) if loc[0] != -1: self.setLocation(*loc) else: gui.trace("Ignoring topLevel geom: %s", geom) # not finished if settings.has_option("GEOM", "fullscreen"): fs = settings.getboolean('GEOM', "fullscreen") gui.trace("Set fullscreen to: %s", fs) if fs: self.setFullscreen() else: self.exitFullscreen() if settings.has_option("GEOM", "minsize"): self.topLevel.ms = settings.get('GEOM', "minsize").split(",") self._getTopLevel().minsize(self.topLevel.ms[0], self.topLevel.ms[1]) gui.trace("Set minsize to: %s", self.topLevel.ms) if settings.has_option("GEOM", "state"): state = settings.get('GEOM', "state") if state in ["withdrawn", "zoomed"]: self._getTopLevel().state(state) if settings.has_option("TOOLBAR", "pinned") and self.tb.inUse: tb = settings.getboolean("TOOLBAR", "pinned") self.setToolbarPinned(tb) gui.trace("Set toolbar to: %s", tb) if "TOGGLES" in settings.sections(): for k in settings.options("TOGGLES"): try: if self.getToggleFrameState(k) != settings.getboolean("TOGGLES", k): self.toggleToggleFrame(k) except ItemLookupError: gui.error("Settings error, invalid TOGGLES name: %s - discarding", k) if "TABS" in settings.sections(): for k in settings.options("TABS"): try: self.setTabbedFrameSelectedTab(k, settings.get("TABS", k)) except ItemLookupError: gui.error("Settings error, invalid TABS name: %s - discarding", k) if "PAGES" in settings.sections(): for k in settings.options("PAGES"): try: self.setPagedWindowPage(k, settings.getint("PAGES", k)) except ItemLookupError: gui.error("Settings error, invalid PAGES name: %s - discarding", k) if "SUBWINDOWS" in settings.sections(): for k in settings.options("SUBWINDOWS"): if settings.getboolean("SUBWINDOWS", k): gui.trace("Loading settings for %s", k) try: tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, k) # process the geom settings if settings.has_option(k, "geometry"): geom = settings.get(k, "geometry") size, loc = gui.SPLIT_GEOM(geom) if size[0] > 1: gui.trace("Setting size: %s", size) tl.geometry("%sx%s" % (size[0], size[1])) tl.shown = True else: gui.trace("Skipping size: %s", size) if loc[0] > -1: gui.trace("Setting location: %s", loc) self.setSubWindowLocation(k, *loc) else: gui.trace("Skipping location: %s", loc) else: gui.trace("No location found") if settings.has_option(k, "minsize"): ms = settings.get(k, "minsize").split(",") self.setMinSize(tl, ms) # set the state - if there' no startWindow if self.startWindow is None: try: tl.state(settings.get(k, "state")) gui.trace("Set state=%s", tl.state()) except: pass # no state found except ItemLookupError: gui.error("Settings error, invalid SUBWINDOWS name: %s - discarding.", k) else: gui.trace("Skipping settings for %s", k) if "EXTERNAL" in settings.sections(): for k in settings.options("EXTERNAL"): self.externalSettings[k] = settings.get("EXTERNAL", k) def stop(self, event=None): """ Closes the GUI. If a stop function is set, will only close the GUI if True """ theFunc = self._getTopLevel().stopFunction if theFunc is None or theFunc(): if self.useSettings: self.saveSettings(self.settingsFile) # stop the after loops self.alive = False self.topLevel.after_cancel(self.pollId) self.topLevel.after_cancel(self.flashId) if self.preloadAnimatedImageId: self.topLevel.after_cancel(self.preloadAnimatedImageId) if self.processQueueId: self.topLevel.after_cancel(self.processQueueId) # stop any animations for key in self.widgetManager.group(WIDGET_NAMES.AnimationID): self.topLevel.after_cancel(self.widgetManager.get(WIDGET_NAMES.AnimationID, key)) # stop any maps for key in self.widgetManager.group(WIDGET_NAMES.Map): self.widgetManager.get(WIDGET_NAMES.Map, key).stopUpdates() # stop any sounds, ignore error when not on Windows try: self.stopSound() except: pass self.topLevel.quit() if not self.fastStop: self.topLevel.destroy() self.__class__.instantiated = False gui.info("--- GUI stopped ---") def setFastStop(self, fast=True): self._fastStop = fast def getFastStop(self): return self._fastStop fastStop = property(getFastStop, setFastStop) ##################################### # Functions for configuring polling events ##################################### def setPollTime(self, time): """ Set a frequency for executing queued functions events will fire in order of being added, after sleeping for time """ self.pollTime = time def registerEvent(self, func): """ Queue a function, to be executed every poll time """ self.events.append(func) def after(self, delay_ms, callback=None, *args): """ wrapper for topLevel after function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled """ return self.topLevel.after(delay_ms, callback, *args) def afterIdle(self, callback, *args): """ wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled """ return self.after_idle(callback, *args) def after_idle(self, callback, *args): """ wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled """ return self.topLevel.after_idle(callback, *args) def afterCancel(self, afterId): """ wrapper for topLevel after_cancel function tries to cancel the specified callback """ return self.after_cancel(afterId) def after_cancel(self, afterId): """ wrapper for topLevel after_cancel function tries to cancel the specified callback """ return self.topLevel.after_cancel(afterId) def queueFunction(self, func, *args, **kwargs): """ adds the specified function & arguments to the event queue Functions in the event queue are actioned by the gui's main thread :param func: the function to call :param *args: any number of ordered arguments :param **kwargs: any number of named arguments :raises Full: if unable to add the function to the queue """ self._loadThreading() if Queue is False: gui.warn("Unable to queueFunction - threading not possible.") else: self.eventQueue.put((5, func, args, kwargs), block=False) def queuePriorityFunction(self, func, *args, **kwargs): """ queues the function with a higher priority - not working yet """ self._loadThreading() if Queue is False: gui.warn("Unable to queueFunction - threading not possible.") else: self.eventQueue.put((1, func, args, kwargs), block=False) def _processEventQueue(self): """ internal function to process events in the event queue put there by queue function """ if not self.alive: return if not self.eventQueue.empty(): priority, func, args, kwargs = self.eventQueue.get() gui.trace("FUNCTION: %s(%s)", func, args) func(*args, **kwargs) self.processQueueId = self.after(self.EVENT_SPEED, self._processEventQueue) def thread(self, func, *args, **kwargs): """ will run the supplied function in a separate thread param func: the function to run """ self._loadThreading() if Queue is False: gui.warn("Unable to queueFunction - threading not possible.") else: t = Thread(group=None, target=func, name=None, args=args, kwargs=kwargs) t.daemon = True t.start() def callback(self, *args, **kwargs): """Shortner for threadCallback.""" return self.threadCallback(*args, **kwargs) def threadCallback(self, func, callback, *args, **kwargs): """Run a given method in a new thread with passed arguments. When func completes call the callback with the result. :param func: Method that returns the result. :param callback: Method that receives the result. :param args: Positional arguments for func. :param kwargs: Keyword args for func. """ def innerThread(func, callback, *args, **kwargs): result = func(*args, **kwargs) self.queueFunction(callback, result) if not callable(func) or not callable(callback): gui.error("Function (or callback) method isn't callable!") return self.thread(innerThread, func, callback, *args, **kwargs) # internal function, called by 'after' function, after sleeping def _poll(self): """ internal function, called by 'after' function, after sleeping """ if not self.alive: return # run any registered actions for e in self.events: # execute the event e() self.pollId = self.topLevel.after(self.pollTime, self._poll) def _windowEvent(self, event): """ called whenever the GUI updates - does nothing """ new_width = self.topLevel.winfo_width() new_height = self.topLevel.winfo_height() def enableEnter(self, func, replace=False): """ Binds <Return> to the specified function - all widgets """ self.bindKey("Return", func, replace) def disableEnter(self): """ unbinds <Return> from all widgets """ self.unbindKey("Return") def _enterWrapper(self, func): if func is None: self.disableEnter() else: self.enableEnter(func, replace=True) enterKey = property(fset=_enterWrapper) def bindKeys(self, keys, func): """ bind the specified keys, to the specified function, for all widgets """ for key in keys: self.bindKey(key, func) def bindKey(self, key, func, replace=False): """ bind the specified key, to the specified function, for all widgets """ if replace: try: self.unbindKey(key) except: pass # for now discard the Event... myF = self.MAKE_FUNC(func, key) binding = EventBinding(key, myF, self._getTopLevel(), menuBinding=False) try: self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding) binding.createBindings() except ItemLookupError: raise ItemLookupError('Unable to bind key ' + binding.displayName + ' - binding already exists') def unbindKeys(self, keys): """ unbinds the specified keys from whatever functions they are bound to """ for key in keys: self.unbindKey(key) def unbindKey(self, key): """ unbinds the specified key from whatever functions it is bound to """ if key[0] == "<": gui.warn("Shortcuts should not include chevrons: %s", key) key= key[1:-1] self.widgetManager.get(WIDGET_NAMES.Bindings, key).removeBindings() self.widgetManager.remove(WIDGET_NAMES.Bindings, key) def _isMouseInWidget(self, w): """ helper - returns True if the mouse is in the specified widget """ l_x = w.winfo_rootx() l_y = w.winfo_rooty() if l_x <= w.winfo_pointerx() <= l_x + \ w.winfo_width() and l_y <= w.winfo_pointery() <= l_y + w.winfo_height(): return True else: return False # function to give a clicked widget the keyboard focus def _grabFocus(self, e): """ gives the specified widget the focus """ e.widget.focus_set() ##################################### # FUNCTIONS for configuring GUI settings ##################################### def setSize(self, geom, height=None, ignoreSettings=None): """ called to update screen geometry can take a geom string, or a width & height can override ignoreSettings if desired """ container = self._getTopLevel() if ignoreSettings is not None: container.ignoreSettings = ignoreSettings try: geom = geom.lower() except: # ignore - other data types allowed pass if geom == "fullscreen": self.setFullscreen() elif geom is not None: if height is not None: geom=(geom, height) elif not isinstance(geom, list) and not isinstance(geom, tuple): geom, loc = gui.SPLIT_GEOM(geom) size = "%sx%s" % (int(geom[0]), int(geom[1])) gui.trace("Setting size: %s", size) # warn the user that their geom is not big enough dims = gui.GET_DIMS(container) if geom[0] < dims["b_width"] or geom[1] < dims["b_height"]: self.trace("Specified dimensions (%s, %s) less than requested dimensions (%s, %s)", geom[0], geom[1], dims["b_width"], dims["b_height"]) # and set it as the minimum size if not hasattr(container, 'ms'): self.setMinSize(container, geom) self.exitFullscreen() container.geometry(size) def getSize(self): container = self._getTopLevel() size, loc = gui.SPLIT_GEOM(container.geometry()) return size size = property(getSize, setSize) def setMinSize(self, container=None, size=None): """ sets a minimum size for the specified container - defaults to the whole GUI """ if container is None: container = self.topLevel if size is None: size = (gui.GET_DIMS(container)["r_width"], gui.GET_DIMS(container)["r_height"]) container.ms = size container.minsize(size[0], size[1]) gui.trace("Minsize set to: %s", size) def setLocation(self, x, y=None, ignoreSettings=None, win=None, up=0): """ called to set the GUI's position on screen """ if win is None: win = self._getTopLevel() gui.SET_LOCATION(x, y, ignoreSettings, win, up) def getLocation(self): container = self._getTopLevel() size, loc = gui.SPLIT_GEOM(container.geometry()) return loc location = property(getLocation, setLocation) def _bringToFront(self, win=None): """ called to make sure this window is on top of other windows """ if win is None: win = self.topLevel top = self.top else: top = win.attributes('-topmost') if self.platform == self.MAC: import subprocess tmpl = 'tell application "System Events" to set frontmost of every process whose unix id is {0} to true' script = tmpl.format(os.getpid()) subprocess.check_call(['/usr/bin/osascript', '-e', script]) win.after( 0, lambda: win.attributes("-topmost", top)) # val=os.system('''/usr/bin/osascript -e 'tell app "Finder" to set frontmost of process "''' + PY_NAME + '''" to true' ''') win.lift() elif self.platform == self.WINDOWS: win.lift() elif self.platform == self.LINUX: win.lift() def setFullscreen(self, title=None): """ sets the specified window to be fullscreen if no title, will set the main GUI """ try: container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) except: container = self._getTopLevel() if not container.isFullscreen: container.isFullscreen = True container.attributes('-fullscreen', True) container.escapeBindId = container.bind('<Escape>', self.MAKE_FUNC(self.exitFullscreen, container), "+") def getFullscreen(self, title=None): if title is None: container = self._getTopLevel() else: container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) return container.isFullscreen def setOnTop(self, stay=True): self._getTopLevel().attributes("-topmost", stay) gui.trace("Staying on top set to: %s", stay) def getOnTop(self): return self._getTopLevel().attributes("-topmost") == 1 top = property(getOnTop, setOnTop) def _changeFullscreen(self, flag): if flag: self.setFullscreen() else: self.exitFullscreen() fullscreen = property(getFullscreen, _changeFullscreen) def exitFullscreen(self, container=None): """ turns off fullscreen mode for the specified window """ if container is None or isinstance(container, UNIVERSAL_STRING): try: container = self.widgetManager.get(WIDGET_NAMES.SubWindow, container) except: container = self._getTopLevel() if container.isFullscreen: container.isFullscreen = False container.attributes('-fullscreen', False) if container.escapeBindId is not None: container.unbind('<Escape>', container.escapeBindId) with PauseLogger(): self._doTitleBar() return True else: return False def setPadX(self, x=0): """ set the current container's external grid padding """ self.containerStack[-1]['padx'] = x def setPadY(self, y=0): """ set the current container's external grid padding """ self.containerStack[-1]['pady'] = y def setPadding(self, x, y=None): """ sets the padding around the border of the current container """ x, y = gui.PARSE_TWO_PARAMS(x, y) self.containerStack[-1]['padx'] = x self.containerStack[-1]['pady'] = y def getPadding(self): return self._getContainerProperty('padx'), self._getContainerProperty('pady') padding = property(getPadding, setPadding) def config(self, **kwargs): self.configure(**kwargs) def configure(self, **kwargs): title = kwargs.pop("title", None) icon = kwargs.pop("icon", None) transparency = kwargs.pop("transparency", None) visible = kwargs.pop("visible", None) top = kwargs.pop("top", None) padding = kwargs.pop("padding", None) inPadding = kwargs.pop("inPadding", None) guiPadding = kwargs.pop("guiPadding", None) size = kwargs.pop("size", None) location = kwargs.pop("location", None) fullscreen = kwargs.pop("fullscreen", None) resizable = kwargs.pop("resizable", None) sticky = kwargs.pop("sticky", None) stretch = kwargs.pop("stretch", None) expand = kwargs.pop("expand", None) row = kwargs.pop("row", None) colspan = kwargs.pop("colspan", None) rowspan = kwargs.pop("rowspan", None) fg = kwargs.pop("fg", None) bg = kwargs.pop("bg", None) font = kwargs.pop("font", None) buttonFont = kwargs.pop("buttonFont", None) labelFont = kwargs.pop("labelFont", None) inputFont = kwargs.pop("inputFont", None) statusFont = kwargs.pop("statusFont", None) ttkTheme = kwargs.pop("ttkTheme", None) editMenu = kwargs.pop("editMenu", None) # two possible names stopFunction = kwargs.pop("stop", kwargs.pop("stopFunction", None)) startFunction = kwargs.pop("start", kwargs.pop("startFunction", None)) fastStop = kwargs.pop("fastStop", None) enterKey = kwargs.pop("enterKey", None) logLevel = kwargs.pop("log", kwargs.pop("logLevel", None)) logFile = kwargs.pop("file", kwargs.pop("logFile", None)) language = kwargs.pop("language", None) for k, v in kwargs.items(): gui.error("Invalid config parameter: %s, %s", k, v) if title is not None: self.title = title if icon is not None: self.icon = icon if transparency is not None: self.transparency = transparency if visible is not None: self.visible = visible if top is not None: self.top = top if padding is not None: self.padding = padding if inPadding is not None: self.inPadding = inPadding if guiPadding is not None: self.guiPadding = guiPadding if size is not None: self.size = size if location is not None: self.location = location if fullscreen is not None: self.fullscreen = fullscreen if resizable is not None: self.resizable = resizable if sticky is not None: self.sticky = sticky if expand is not None: self.expand = expand if stretch is not None: self.stretch = stretch if row is not None: self.row = row if rowspan is not None: self.rowspan = rowspan if colspan is not None: self.colspan = colspan if fg is not None: self.fg = fg if bg is not None: self.bg = bg if font is not None: self.font = font if labelFont is not None: self.labelFont = labelFont if buttonFont is not None: self.buttonFont = buttonFont if inputFont is not None: self.inputFont = inputFont if statusFont is not None: self.statusFont = statusFont if ttkTheme is not None: self.ttkTheme = ttkTheme if editMenu is not None: self.editMenu = editMenu if stopFunction is not None: self.stopFunction = stopFunction if startFunction is not None: self.startFunction = startFunction if fastStop is not None: self.fastStop = fastStop if enterKey is not None: self.enterKey = enterKey if logLevel is not None: self.logLevel = logLevel if logFile is not None: self.logFile = logFile if language is not None: self.language = language def setGuiPadding(self, x, y=None): """ sets the padding around the border of the GUI """ x, y = gui.PARSE_TWO_PARAMS(x, y) self.containerStack[0]['container'].config(padx=x, pady=y) def getGuiPadding(self): return int(str(self.containerStack[0]['container'].cget('padx'))), int(str(self.containerStack[0]['container'].cget('pady'))) guiPadding = property(getGuiPadding, setGuiPadding) # sets the current containers internal padding def setIPadX(self, x=0): self.setInPadX(x) def setIPadY(self, y=0): self.setInPadY(y) def setIPadding(self, x, y=None): self.setInPadding(x, y) def setInPadX(self, x=0): self.containerStack[-1]['ipadx'] = x def setInPadY(self, y=0): self.containerStack[-1]['ipady'] = y def setInPadding(self, x, y=None): x, y = gui.PARSE_TWO_PARAMS(x, y) self.containerStack[-1]['ipadx'] = x self.containerStack[-1]['ipady'] = y def getInPadding(self): return self._getContainerProperty('ipadx'), self._getContainerProperty('ipady') inPadding = property(getInPadding, setInPadding) # set an override sticky for this container def setSticky(self, sticky): self.containerStack[-1]['sticky'] = sticky def getSticky(self): return self._getContainerProperty('sticky') # property for setTitle sticky = property(getSticky, setSticky) # this tells widgets what to do when GUI is resized def setStretch(self, exp): self.setExpand(exp) def getStretch(self): return self.getExpand() stretch = property(getStretch, setStretch) def getExpand(self): return self._getContainerProperty('expand') def setExpand(self, exp): if exp is None or exp.lower() == "none": self.containerStack[-1]['expand'] = "NONE" elif exp.lower() == "row": self.containerStack[-1]['expand'] = "ROW" elif exp.lower() == "column": self.containerStack[-1]['expand'] = "COLUMN" else: self.containerStack[-1]['expand'] = "ALL" expand = property(getExpand, setExpand) def RANDOM_COLOUR(self): return self.getRandomColour() def getRandomColour(self): """ generates a random colour """ self._loadRandom() de=("%02x"%random.randint(0,255)) re=("%02x"%random.randint(0,255)) we=("%02x"%random.randint(0,255)) return "#"+de+re+we randomColour = property(getRandomColour) def getFonts(self): fonts = list(tkFont.families()) fonts.sort() return fonts fonts = property(getFonts) def increaseFont(self): self.increaseLabelFont() self.increaseButtonFont() def decreaseFont(self): self.decreaseLabelFont() self.decreaseButtonFont() def increaseButtonFont(self): self.setButtonFont(size=self._buttonFont['size'] + 1) def decreaseButtonFont(self): self.setButtonFont(size=self._buttonFont['size'] - 1) def increaseLabelFont(self): self.setLabelFont(size=self._labelFont['size'] + 1) def decreaseLabelFont(self): self.setLabelFont(size=self._labelFont['size'] - 1) def setFont(self, *args, **kwargs): self.setInputFont(*args, **kwargs) self.setLabelFont(*args, **kwargs) self.setButtonFont(*args, **kwargs) def getFont(self): return self._getContainerProperty('labelFont').actual() font = property(getFont, setFont) def _fontHelper(self, font, *args, **kwargs): if len(args) > 0: if isinstance(args[0], int): kwargs={'size':args[0]} elif isinstance(args[0], dict): kwargs=args[0] elif isinstance(args[0], tkFont.Font): gui.trace("%s set to new object", font) if font != "statusFont": self.containerStack[-1][font]=args[0] else: self._statusFont=args[0] return None if font != "statusFont": self._getContainerProperty(font).config(**kwargs) f = self._getContainerProperty(font).actual()['family'].lower() else: self._statusFont.config(**kwargs) f = self._statusFont.actual()['family'].lower() if 'family' in kwargs and kwargs['family'].lower() != f: gui.error("Failed to adjust %s to %s.", font, kwargs['family']) return kwargs def setInputFont(self, *args, **kwargs): self._fontHelper('inputFont', *args, **kwargs) def getInputFont(self): return self._getContainerProperty('inputFont').actual() inputFont = property(getInputFont, setInputFont) def setStatusFont(self, *args, **kwargs): self._fontHelper('statusFont', *args, **kwargs) def getStatusFont(self): return self._statusFont.actual() statusFont = property(getStatusFont, setStatusFont) def setButtonFont(self, *args, **kwargs): self._fontHelper('buttonFont', *args, **kwargs) def getButtonFont(self): return self._getContainerProperty('buttonFont').actual() buttonFont = property(getButtonFont, setButtonFont) def setLabelFont(self, *args, **kwargs): kwargs = self._fontHelper('labelFont', *args, **kwargs) if kwargs is not None: self.tableFont.config(**kwargs) # need better way to register font change events on tables for k, v in self.widgetManager.group(WIDGET_NAMES.Table).items(): v.config(font=self.tableFont) linkArgs = kwargs.copy() linkArgs['underline'] = True linkArgs['weight'] = 'bold' self._linkFont.config(**linkArgs) def getLabelFont(self): return self._getContainerProperty('labelFont').actual() labelFont = property(getLabelFont, setLabelFont) # need to set a default colour for container # then populate that field # then use & update that field accordingly # all widgets will then need to use it # and here we update all.... def setFg(self, colour, override=False): if not self.ttkFlag: self.containerStack[-1]['fg']=colour gui.SET_WIDGET_FG(self._getContainerProperty('container'), colour, override) for child in self._getContainerProperty('container').winfo_children(): if not self._isWidgetContainer(child): gui.SET_WIDGET_FG(child, colour, override) else: gui.trace("In ttk mode - trying to set FG to %s", colour) self.ttkStyle.configure("TLabel", foreground=colour) self.ttkStyle.configure("TFrame", foreground=colour) def getBg(self): if self._getContainerProperty('type') == WIDGET_NAMES.RootPage: if not self.ttkFlag: return self.bgLabel.cget("bg") else: return self.bgLabel.cget("background") else: if not self.ttkFlag: return self._getContainerProperty('container').cget("bg") else: return None def getFg(self): return self._getContainerProperty("fg") fg = property(getFg, setFg) # self.topLevel = Tk() # self.appWindow = CanvasDnd, fills all of self.topLevel # self.tb = Frame, at top of appWindow # self.container = Frame, at bottom of appWindow => C_ROOT container # self.bglabel = Label, filling all of container def setBg(self, colour, override=False, tint=False): if not self.ttkFlag: if self._getContainerProperty('type') == WIDGET_NAMES.RootPage: # removed this - it makes the screen do funny stuff # self.appWindow.config(background=colour) self.bgLabel.config(background=colour) self._getContainerProperty('container').config(background=colour) for child in self._getContainerProperty('container').winfo_children(): if not self._isWidgetContainer(child): # horrible hack to deal with weird ScrolledText # winfo_children returns ScrolledText as a Frame #Â therefore can't call some functions # this gets the ScrolledText version if gui.GET_WIDGET_CLASS(child) == "Frame": for val in self.widgetManager.group(WIDGET_NAMES.TextArea).values(): if str(val) == str(child): child = val break gui.SET_WIDGET_BG(child, colour, override, tint) else: gui.trace("In ttk mode - trying to set BG to %s", colour) self.ttkStyle.configure(".", background=colour) bg = property(getBg, setBg) @staticmethod def _isWidgetContainer(widget): try: if widget.isContainer: return True except: pass return False def setResizable(self, canResize=True): self._getTopLevel().isResizable = canResize if self._getTopLevel().isResizable: self._getTopLevel().resizable(True, True) else: self._getTopLevel().resizable(False, False) def getResizable(self): return self._getTopLevel().isResizable resizable = property(getResizable, setResizable) def _doTitleBar(self): if self.platform == self.MAC: self.warn("Title bar hiding doesn't work on MAC - app may become unresponsive.") elif self.platform == self.LINUX: self.warn("Title bar hiding doesn't work on LINUX - app may become unresponsive.") self._getTopLevel().overrideredirect(not self.hasTitleBar) def hideTitleBar(self): self.hasTitleBar = False self._doTitleBar() def showTitleBar(self): self.hasTitleBar = True self._doTitleBar() # function to set the window's title def setTitle(self, title): self._getTopLevel().title(title) # function to get the window title def getTitle(self): return self._getTopLevel().title() # property for setTitle title = property(getTitle, setTitle) # set an icon def setIcon(self, image): container = self._getTopLevel() container.winIcon = image if image.endswith('.ico'): container.wm_iconbitmap(image) else: icon = self._getImage(image) container.iconphoto(True, icon) def getIcon(self): container = self._getTopLevel() return container.winIcon # property for setTitle icon = property(getIcon, setIcon) def _getCanvas(self, param=-1): if len(self.containerStack) > 1 and self.containerStack[param]['type'] == WIDGET_NAMES.SubWindow: return self.containerStack[param]['container'] elif len(self.containerStack) > 1: return self._getCanvas(param-1) else: return self.topLevel def _getTopLevel(self): if len(self.containerStack) > 1 and self._getContainerProperty('type') == WIDGET_NAMES.SubWindow: return self._getContainerProperty('container') else: return self.topLevel # make the window transparent (between 0 & 1) def setTransparency(self, percentage): if self.platform == self.LINUX: self.warn("Transparency not supported on LINUX") else: if percentage > 1: percentage = float(percentage) / 100 self._getTopLevel().attributes("-alpha", percentage) def getTransparency(self): return self._getTopLevel().attributes("-alpha") * 100 # property for setTransparency transparency = property(getTransparency, setTransparency) ############################## # functions to deal with tabbing and right clicking ############################## def _focusNextWindow(self, event): event.widget.tk_focusNext().focus_set() nowFocus = self.topLevel.focus_get() if isinstance(nowFocus, Entry): nowFocus.select_range(0, END) return("break") def _focusLastWindow(self, event): event.widget.tk_focusPrev().focus_set() nowFocus = self.topLevel.focus_get() if isinstance(nowFocus, Entry): nowFocus.select_range(0, END) return("break") # creates relevant bindings on the widget def _addRightClickMenu(self, widget): if self.platform in [self.WINDOWS, self.LINUX]: widget.bind('<Button-3>', self._rightClick) else: widget.bind('<Button-2>', self._rightClick) def _rightClick(self, event, menu="EDIT"): event.widget.focus() if menu == "EDIT": if self._prepareCopyAndPasteMenu(event): self.widgetManager.get(WIDGET_NAMES.Menu, menu).focus_set() self.widgetManager.get(WIDGET_NAMES.Menu, menu).post(event.x_root - 10, event.y_root - 10) else: self.widgetManager.get(WIDGET_NAMES.Menu, menu).focus_set() self.widgetManager.get(WIDGET_NAMES.Menu, menu).post(event.x_root - 10, event.y_root - 10) return "break" ##################################### # FUNCTION to configure widgets ##################################### def configureAllWidgets(self, kind, option, value): items = list(self.widgetManager.group(kind)) self.configureWidgets(kind, items, option, value) def configureWidgets(self, kind, names, option, value): if not isinstance(names, list): self.configureWidget(kind, names, option, value) else: for widg in names: # incase 2D array, eg. buttons if isinstance(widg, list): for widg2 in widg: self.configureWidget(kind, widg2, option, value) else: self.configureWidget(kind, widg, option, value) def getWidget(self, kind, name, val=None): # if val is set (RadioButtons) - append it if val is not None: name+= "-" + val return self.widgetManager.get(kind, name) def getWidgetProperty(self, kind, name, val, prop): return self.getWidget(kind, name, val).cget(prop) def addWidget(self, title, widg, row=None, column=0, colspan=0, rowspan=0): ''' adds a generic widget to the appJar grid manager ''' self.widgetManager.verify(WIDGET_NAMES.Widget, title) self._positionWidget(widg, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Widget, title, widg) def _getWidgetList(self, kind, name, limit): # gets a list of items of this type # limit is used to only get a single radio button - for events if kind == WIDGET_NAMES.RadioButton: items = self.widgetManager.group(kind) new_items = [] for k, v in items.items(): if k.startswith(name+"-"): new_items.append(v) if len(new_items) == 0: raise Exception("No RadioButtons found with that name " + name) else: items = new_items #Â stops multiple events... if limit: items = [items[0]] else: # get the list of items for this type, and validate the widget is in the list self.widgetManager.check(kind, name) items = self.widgetManager.group(kind) items = [items[name]] return items def configureWidget(self, kind, name, option, value, key=None, deprecated=False): gui.trace("Configuring: %s of %s with %s of %s", name, kind, option, value) # warn about deprecated functions if deprecated: self.warn("Deprecated config function (%s) used for %s -> %s use %s deprecated", option, WIDGET_NAMES.name(kind), name, deprecated) # will return multiple items if radio button... items = self._getWidgetList(kind, name, limit=option in ['change', 'command']) # loop through each item, and try to reconfigure it #Â this will often fail - widgets have varied config options for item in items: try: if option == 'background': gui.SET_WIDGET_BG(item, value, True) elif option == 'foreground': gui.SET_WIDGET_FG(item, value, True) elif option == 'disabledforeground': item.config(disabledforeground=value) elif option == 'disabledbackground': item.config(disabledbackground=value) elif option == 'activeforeground': item.config(activeforeground=value) elif option == 'activebackground': item.config(activebackground=value) elif option == 'inactiveforeground': if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]: item.config(inactiveforeground=value) else: self.warn("Error configuring %s: can't set inactiveforeground", name ) elif option == 'inactivebackground': if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]: item.config(inactivebackground=value) else: self.warn("Error configuring %s: can't set inactivebackground", name) elif option == 'width': item.config(width=value) elif option == 'height': item.config(height=value) elif option == 'state': # make entries readonly - can still copy/paste but = None if kind == WIDGET_NAMES.Entry: if value == "disabled" and hasattr(item, 'but'): but = item.but item.unbind("<Button-1>") value = "readonly" elif value == 'normal' and hasattr(item, 'but') and item.cget('state') != 'normal': but = item.but item.bind("<Button-1>", item.click_command, "+") if self.ttkFlag: gui.trace("%s configured with ttk state %s", name, value) item.state([value]) if but is not None: but.state([value]) else: item.config(state=value) if but is not None: but.config(state=value) elif option == 'relief': item.config(relief=value) elif option == 'style': if self.ttkFlag: gui.trace("%s configured with ttk style %s", name, value) item.config(style=value) else: self.warn("Error configuring %s: can't set ttk style, not in ttk mode.", name) elif option in ['align', 'anchor']: if kind == WIDGET_NAMES.Entry or gui.GET_WIDGET_CLASS(item) == 'SelectableLabel': if value == W: value = LEFT elif value == E: value = RIGHT item.config(justify=value) elif kind == WIDGET_NAMES.LabelFrame: item.config(labelanchor=value) else: if value == LEFT: value = "w" elif value == RIGHT: value = "e" item.config(anchor=value) elif option == 'cursor': item.config(cursor=value) elif option == 'tooltip': self._addTooltip(item, value) elif option == 'disableTooltip': self._disableTooltip(item) elif option == 'enableTooltip': self._enableTooltip(item) elif option == "focus": item.focus_set() if kind == WIDGET_NAMES.Entry: if not self.ttkFlag: item.icursor(END) item.xview(END) else: item.icursor(END) item.xview(len(item.get())) # event bindings elif option == 'over': self._bindOverEvent(kind, name, item, value, option, key) elif option == 'drag': self._bindDragEvent(kind, name, item, value, option, key) elif option in ['command', "change", "submit"]: self._bindEvent(kind, name, item, value, option, key) elif option == 'sticky': info = {} # need to reposition the widget in its grid if self._widgetHasContainer(kind, item): # pack uses LEFT & RIGHT & BOTH info["side"] = value if value.lower() == "both": info["expand"] = 1 info["side"] = "right" else: info["expand"] = 0 else: # grid uses E+W if value.lower() == "left": side = W elif value.lower() == "right": side = E elif value.lower() == "both": side = W + E else: side = value.upper() info["sticky"] = side self._repackWidget(item, info) elif option == 'padding': if value[1] is None: item.config(padx=value[0][0], pady=value[0][1]) else: item.config(padx=value[0], pady=value[1]) elif option == 'ipadding': if value[1] is None: item.config(ipadx=value[0][0], ipady=value[0][1]) else: item.config(ipadx=value[0], ipady=value[1]) elif option == 'rightClick': self._bindRightClick(item, value) elif option == 'internalDrop': self._registerInternalDropTarget(item, value) elif option == 'internalDrag': self._registerInternalDragSource(kind, name, item, value) elif option == 'externalDrop': self._registerExternalDropTarget(name, item, value[0], value[1]) elif option == 'externalDrag': self._registerExternalDragSource(name, item, value) except TclError as e: self.warn("Error configuring %s: %s", name, str(e)) # generic function for over events def _validateFunctionList(self, functions, mode): if type(functions) == tuple: functions = list(functions) elif type(functions) != list: functions = [functions] if len(functions) == 1: functions.append(None) if len(functions) != 2: raise Exception("Invalid arguments, set<widget> %s Function requires 1 or 2 functions to be passed in.", mode) return functions def _bindOverEvent(self, kind, name, widget, functions, eventType, key=None): functions = self._validateFunctionList(functions, "Over") if functions[0] is not None: widget.bind("<Enter>", self.MAKE_FUNC(functions[0], name), add="+") if functions[1] is not None: widget.bind("<Leave>", self.MAKE_FUNC(functions[1], name), add="+") # generic function for drag events def _bindDragEvent(self, kind, name, widget, functions, eventType, key=None): functions = self._validateFunctionList(functions, "Drag") if kind == WIDGET_NAMES.Label: widget.config(cursor="fleur") def getLabel(f): # loop through all labels items = self.widgetManager.group(kind) for key, value in items.items(): if self._isMouseInWidget(value): self.MAKE_FUNC(f,key)() return if functions[0] is not None: widget.bind("<ButtonPress-1>", self.MAKE_FUNC(functions[0], name), add="+") if functions[1] is not None: widget.bind("<ButtonRelease-1>", self.MAKE_FUNC(getLabel, functions[1]), add="+") else: self.error("Only able to bind drag events to labels") # generic function for change/submit/events def _bindEvent(self, kind, name, widget, function, eventType, key=None): # this will discard the scale value, as default function # can't handle it if kind == WIDGET_NAMES.Scale: cmd = self.MAKE_FUNC(function, name) widget.cmd_id = widget.var.trace('w', cmd) widget.cmd = cmd elif kind == WIDGET_NAMES.OptionBox: if widget.kind == "ticks": vals = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, name, group=WidgetManager.VARS) for o in vals: cmd = self.MAKE_FUNC(function, name) vals[o].cmd_id = vals[o].trace('w', cmd) vals[o].cmd = cmd else: cmd = self.MAKE_FUNC(function, name) # need to trace the variable?? widget.cmd_id = widget.var.trace('w', cmd) widget.cmd = cmd elif kind in [WIDGET_NAMES.Entry, WIDGET_NAMES.FileEntry, WIDGET_NAMES.DirectoryEntry]: if eventType == "change": # not populated by change/submit if key is None: key = name cmd = self.MAKE_FUNC(function, key) # get Entry variable var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) var.cmd_id = var.trace('w', cmd) var.cmd = cmd else: # not populated by change/submit if key is None: key = name sbm = self.MAKE_FUNC(function, key) widget.sbm_id = widget.bind('<Return>', sbm) widget.sbm = sbm elif kind == WIDGET_NAMES.TextArea: if eventType == "change": # get Entry variable cmd = self.MAKE_FUNC(function, name) widget.bindChangeEvent(cmd) elif kind == WIDGET_NAMES.Button: if eventType == "change": self.warn("Error configuring %s : can't set a change function", name) else: widget.config(command=self.MAKE_FUNC(function, name)) widget.bind('<Return>', self.MAKE_FUNC(function, name)) # make labels clickable, add a cursor, and change the look elif kind == WIDGET_NAMES.Label or kind == WIDGET_NAMES.Image: if eventType in ["command", "submit"]: if self.platform == self.MAC: widget.config(cursor="pointinghand") elif self.platform in [self.WINDOWS, self.LINUX]: widget.config(cursor="hand2") cmd = self.MAKE_FUNC(function, name) widget.bind("<Button-1>", cmd, add="+") widget.cmd = cmd # these look good, but break when dialogs take focus #up = widget.cget("relief").lower() # down="sunken" # make it look like it's pressed #widget.bind("<Button-1>",lambda e: widget.config(relief=down), add="+") #widget.bind("<ButtonRelease-1>",lambda e: widget.config(relief=up)) elif eventType == "change": self.warn("Error configuring %s : can't set a change function", name) elif kind == WIDGET_NAMES.ListBox: cmd = self.MAKE_FUNC(function, name) widget.bind('<<ListboxSelect>>', cmd) widget.cmd = cmd elif kind in [WIDGET_NAMES.RadioButton]: cmd = self.MAKE_FUNC(function, name) # get rb variable var = self.widgetManager.get(WIDGET_NAMES.RadioButton, name, group=WidgetManager.VARS) # only allow one trace to be bound # users are more likely to call multiple binds on radios # because they all share one var if hasattr(var, "cmd_id"): var.trace_vdelete('w', var.cmd_id) var.cmd_id = var.trace('w', cmd) var.cmd = cmd elif kind in [WIDGET_NAMES.Properties, WIDGET_NAMES.FrameStack, WIDGET_NAMES.Table]: cmd = self.MAKE_FUNC(function, name) widget.setChangeFunction(cmd) elif kind == WIDGET_NAMES.SpinBox: widget.cmd = self.MAKE_FUNC(function, name) widget.cmd_id = widget.var.trace("w", widget.cmd) elif kind == WIDGET_NAMES.PanedFrame: widget.cmd = self.MAKE_FUNC(function, name) widget.bind("<Configure>", widget.cmd) else: if kind not in [WIDGET_NAMES.CheckBox]: self.warn("Unmanaged binding of %s to %s", eventType, name) cmd = self.MAKE_FUNC(function, name) widget.config(command=cmd) widget.cmd = cmd # dynamic way to create the configuration functions def _buildConfigFuncs(self): # loop through all the available widgets # and make all the below functons for each one for v in WIDGET_NAMES.funcs(): k = WIDGET_NAMES.get(v) exec( "def set" + v + "Bg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'background', val)") exec("gui.set" + v + "Bg=set" + v + "Bg") exec( "def set" + v + "Fg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'foreground', val)") exec("gui.set" + v + "Fg=set" + v + "Fg") exec( "def set" + v + "DisabledFg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'disabledforeground', val)") exec("gui.set" + v + "DisabledFg=set" + v + "DisabledFg") exec( "def set" + v + "DisabledBg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'disabledbackground', val)") exec("gui.set" + v + "DisabledBg=set" + v + "DisabledBg") exec( "def set" + v + "ActiveFg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'activeforeground', val)") exec("gui.set" + v + "ActiveFg=set" + v + "ActiveFg") exec( "def set" + v + "ActiveBg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'activebackground', val)") exec("gui.set" + v + "ActiveBg=set" + v + "ActiveBg") exec( "def set" + v + "InactiveFg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'inactiveforeground', val)") exec("gui.set" + v + "InactiveFg=set" + v + "InactiveFg") exec( "def set" + v + "InactiveBg(self, name, val): self.configureWidgets(" + str(k) + ", name, 'inactivebackground', val)") exec("gui.set" + v + "InactiveBg=set" + v + "InactiveBg") exec( "def set" + v + "Width(self, name, val): self.configureWidgets(" + str(k) + ", name, 'width', val)") exec("gui.set" + v + "Width=set" + v + "Width") exec( "def set" + v + "Height(self, name, val): self.configureWidgets(" + str(k) + ", name, 'height', val)") exec("gui.set" + v + "Height=set" + v + "Height") exec( "def set" + v + "State(self, name, val): self.configureWidgets(" + str(k) + ", name, 'state', val)") exec("gui.set" + v + "State=set" + v + "State") exec( "def set" + v + "Padding(self, name, x, y=None): self.configureWidgets(" + str(k) + ", name, 'padding', [x, y])") exec("gui.set" + v + "Padding=set" + v + "Padding") exec( "def set" + v + "IPadding(self, name, x, y=None): self.configureWidgets(" + str(k) + ", name, 'ipadding', [x, y])") exec("gui.set" + v + "IPadding=set" + v + "IPadding") exec( "def set" + v + "InPadding(self, name, x, y=None): self.configureWidgets(" + str(k) + ", name, 'ipadding', [x, y])") exec("gui.set" + v + "InPadding=set" + v + "InPadding") # drag and drop stuff exec( "def set" + v + "DropTarget(self, name, function=None, replace=True): self.configureWidgets(" + str(k) + ", name, 'externalDrop', [function, replace])") exec("gui.set" + v + "DropTarget=set" + v + "DropTarget") exec( "def set" + v + "DragSource(self, name, function=None): self.configureWidgets(" + str(k) + ", name, 'externalDrag', function)") exec("gui.set" + v + "DragSource=set" + v + "DragSource") exec( "def register" + v + "Draggable(self, name, function=None): self.configureWidgets(" + str(k) + ", name, 'internalDrag', function)") exec("gui.register" + v + "Draggable=register" + v + "Draggable") exec( "def register" + v + "Droppable(self, name, function=None): self.configureWidgets(" + str(k) + ", name, 'internalDrop', function)") exec("gui.register" + v + "Droppable=register" + v + "Droppable") exec( "def set" + v + "Style(self, name, val): self.configureWidget(" + str(k) + ", name, 'style', val)") exec("gui.set" + v + "Style=set" + v + "Style") # might not all be necessary, could make exclusion list exec( "def set" + v + "Relief(self, name, val): self.configureWidget(" + str(k) + ", name, 'relief', val)") exec("gui.set" + v + "Relief=set" + v + "Relief") exec( "def set" + v + "Align(self, name, val): self.configureWidget(" + str(k) + ", name, 'align', val)") exec("gui.set" + v + "Align=set" + v + "Align") exec( "def set" + v + "Anchor(self, name, val): self.configureWidget(" + str(k) + ", name, 'anchor', val)") exec("gui.set" + v + "Anchor=set" + v + "Anchor") exec( "def set" + v + "Tooltip(self, name, val): self.configureWidget(" + str(k) + ", name, 'tooltip', val)") exec("gui.set" + v + "Tooltip=set" + v + "Tooltip") exec( "def disable" + v + "Tooltip(self, name): self.configureWidget(" + str(k) + ", name, 'disableTooltip', None)") exec("gui.disable" + v + "Tooltip=disable" + v + "Tooltip") exec( "def enable" + v + "Tooltip(self, name): self.configureWidget(" + str(k) + ", name, 'enableTooltip', None)") exec("gui.enable" + v + "Tooltip=enable" + v + "Tooltip") # function setters exec( "def set" + v + "ChangeFunction(self, name, val): self.configureWidget(" + str(k) + ", name, 'change', val)") exec("gui.set" + v + "ChangeFunction=set" + v + "ChangeFunction") exec( "def set" + v + "SubmitFunction(self, name, val): self.configureWidget(" + str(k) + ", name, 'submit', val)") exec("gui.set" + v + "SubmitFunction=set" + v + "SubmitFunction") exec( "def set" + v + "DragFunction(self, name, val): self.configureWidget(" + str(k) + ", name, 'drag', val)") exec("gui.set" + v + "DragFunction=set" + v + "DragFunction") exec( "def set" + v + "OverFunction(self, name, val): self.configureWidget(" + str(k) + ", name, 'over', val)") exec("gui.set" + v + "OverFunction=set" + v + "OverFunction") # http://infohost.nmt.edu/tcc/help/pubs/tkinter/web/cursors.html exec( "def set" + v + "Cursor(self, name, val): self.configureWidget(" + str(k) + ", name, 'cursor', val)") exec("gui.set" + v + "Cursor=set" + v + "Cursor") exec( "def set" + v + "Focus(self, name): self.configureWidget(" + str(k) + ", name, 'focus', None)") exec("gui.set" + v + "Focus=set" + v + "Focus") # change the stickyness exec( "def set" + v + "Sticky(self, name, pos): self.configureWidget(" + str(k) + ", name, 'sticky', pos)") exec("gui.set" + v + "Sticky=set" + v + "Sticky") # add right click exec( "def set" + v + "RightClick(self, name, menu): self.configureWidget(" + str(k) + ", name, 'rightClick', menu)") exec("gui.set" + v + "RightClick=set" + v + "RightClick") # functions to manage widgets exec( "def show" + v + "(self, name): self.showWidgetType(" + str(k) + ", name)") exec("gui.show" + v + "=show" + v) exec( "def hide" + v + "(self, name, collapse=False): self.hideWidgetType(" + str(k) + ", name, collapse)") exec("gui.hide" + v + "=hide" + v) exec( "def remove" + v + "(self, name, collapse=False): self.removeWidgetType(" + str(k) + ", name, collapse)") exec("gui.remove" + v + "=remove" + v) exec( "def move" + v + "(self, name, row=None, column=0, colspan=0, rowspan=0, sticky=W+E): self.moveWidgetType(" + str(k) + ", name, row, column, colspan, rowspan, sticky)") exec("gui.move" + v + "=move" + v) exec( "def empty" + v + "(self, name): self._emptyContainerType(" + str(k) + ", name)") exec("gui.empty" + v + "=empty" + v) # convenience functions for enable/disable # might not all be necessary, could make exclusion list exec( "def enable" + v + "(self, name): self.configureWidget(" + str(k) + ", name, 'state', 'normal')") exec("gui.enable" + v + "=enable" + v) exec( "def disable" + v + "(self, name): self.configureWidget(" + str(k) + ", name, 'state', 'disabled')") exec("gui.disable" + v + "=disable" + v) # group functions exec( "def set" + v + "Widths(self, names, val): self.configureWidgets(" + str(k) + ", names, 'width', val)") exec("gui.set" + v + "Widths=set" + v + "Widths") exec( "def setAll" + v + "Widths(self, val): self.configureAllWidgets(" + str(k) + ", 'width', val)") exec("gui.setAll" + v + "Widths=setAll" + v + "Widths") exec( "def set" + v + "Heights(self, names, val): self.configureWidgets(" + str(k) + ", names, 'height', val)") exec("gui.set" + v + "Heights=set" + v + "Heights") exec( "def setAll" + v + "Heights(self, val): self.configureAllWidgets(" + str(k) + ", 'height', val)") exec("gui.setAll" + v + "Heights=setAll" + v + "Heights") exec( "def get" + v + "Widget(self, name, val=None): return self.getWidget(" + str(k) + ", name, val)") exec("gui.get" + v + "Widget=get" + v + "Widget") exec( "def get" + v + "Bg(self, name, val=None): return self.getWidgetProperty(" + str(k) + ", name, val, 'bg')") exec("gui.get" + v + "Bg=get" + v + "Bg") ##################################### # Â FUNCTION to hide/show/remove widgets ##################################### def _widgetHasContainer(self, kind, item): if kind in ( WIDGET_NAMES.Scale, WIDGET_NAMES.Entry, WIDGET_NAMES.SpinBox, WIDGET_NAMES.OptionBox, WIDGET_NAMES.Label) and item.inContainer: return True else: return False def _cloneWidget(self, widget, parent): clone = widget.__class__(parent) for key in widget.configure(): clone.configure({key: widget.cget(key)}) origProps = widget.__dict__ for x in origProps: if x not in ['_w', '_tclCommands', '_name', 'master', 'tk']: setattr(clone, x, origProps[x]) return clone def moveWidgetType(self, kind, name, row=None, column=0, colspan=0, rowspan=0, sticky=W+E): self.hideWidgetType(kind, name, collapse=True) widget = self.widgetManager.get(kind, name) container = self.getContainer() if container != widget.master: widget = self._cloneWidget(widget, container) self.widgetManager.update(kind, name, widget) self._positionWidget(widget, row, column, colspan, rowspan, sticky, updateBg=False) return widget def hideWidgetType(self, kind, name, collapse=False): items = self._getWidgetList(kind, name, limit=False) for item in items: if self._widgetHasContainer(kind, item): gui.trace("Hiding widget in container: %s", name) widget = item.master if hasattr(widget, "inContainer") and widget.inContainer: gui.trace("Have container in container") widget = widget.master try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = True except: pass else: gui.trace("Hiding widget: %s", name) # if kind in [WIDGET_NAMES.RadioButton]: # for rb in item: # if rb.text == name: # widget = rb widget = item if "in" in widget.grid_info(): gui.trace("Widget hidden: %s", name) info = widget.grid_info() widget.grid_remove() if collapse: widget.master.grid_rowconfigure(info["row"], minsize=0, weight=0) else: gui.trace("Hiding failed - %s not showing", name) def showWidgetType(self, kind, name): items = self._getWidgetList(kind, name, limit=False) for item in items: if self._widgetHasContainer(kind, item): gui.trace("Showing widget in container: %s", name) widget = item.master if hasattr(widget, "inContainer") and widget.inContainer: gui.trace("Have container in container") widget = widget.master try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = False except: pass else: msg = "Showing widget" widget = item # only show the widget, if it's not already showing if "in" not in widget.grid_info(): gui.trace("Widget shown: %s", name) widget.grid() # self._updateLabelBoxes(name, widget.grid_info()['column']) else: gui.trace("Showing failed - %s already showing", name) def emptySubWindow(self, title): # not generated by function generator self._emptyContainerType(WIDGET_NAMES.SubWindow, title) def emptyCurrentContainer(self): cConf = self.containerStack[-1] kind = WIDGET_NAMES.name(cConf['type']) title = cConf['title'] gui.trace('Emptying current container %s: %s', kind, title) if not self._emptyContainerObj(cConf['container']): gui.trace('No widgets found in current container %s: %s to empty', kind, title) cConf = self._prepContainer(cConf["title"], cConf["type"], cConf["container"], 0, 1) self.containerStack[-1] = cConf def _emptyContainerType(self, kind, title): kind = WIDGET_NAMES.name(kind) gui.trace('Emptying %s: %s', kind, title) cName = kind + "__" + title try: cConf = self.widgetManager.get(WIDGET_NAMES.ContainerLog, cName) except KeyError: raise Exception("Attempted to empty invalid " + kind + ": " + str(title)) if not self._emptyContainerObj(cConf['container']): gui.trace('No widgets found in %s: %s to empty', kind, title) def removeAllWidgets(self, current=False, sub=False): if current: self.emptyCurrentContainer() else: gui.trace('Removing all widgets from appJar') if sub: self.destroyAllSubWindows() containerData = self.containerStack[0] container = containerData['container'] self._emptyContainerObj(container) # reset container values containerData = self._prepContainer(containerData["title"], containerData["type"], containerData["container"], 0, 1) self.containerStack[0] = containerData # self.widgetManager.reset(WIDGET_NAMES.keepers) # self.setSize(None) def _emptyContainerObj(self, container): widgs = False for child in container.winfo_children(): self.cleanseWidgets(child) widgs = True # reset the grid measurements for i in range(Grid.grid_size(container)[0]): container.columnconfigure(i, minsize=0, weight=0, pad=0) for i in range(Grid.grid_size(container)[1]): container.rowconfigure(i, minsize=0, weight=0, pad=0) return widgs def removeWidgetType(self, kind, name, collapse=False): if kind == WIDGET_NAMES.RadioButton: gui.error("Can't remove widget %s - %s", kind, name) return item = self.widgetManager.get(kind, name) # if it's a flasher, remove it if item in self.widgetManager.group(WIDGET_NAMES.FlashLabel): gui.trace("Remove flash label: %s", name) self.widgetManager.remove(WIDGET_NAMES.FlashLabel, item) if len(self.widgetManager.group(WIDGET_NAMES.FlashLabel)) == 0: self.doFlash = False # animated images... if self._widgetHasContainer(kind, item): gui.trace("Remove widget (%s) in container: %s", kind, name) parent = item.master # is it a container in a labelBox? # if so - remove & destroy the labelBox if hasattr(parent, "inContainer") and parent.inContainer: gui.trace("Container in container") labParent = parent.master self.widgetManager.remove(WIDGET_NAMES.FrameBox, labParent) self.widgetManager.remove(WIDGET_NAMES.Label, name) self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name) labParent.grid_forget() labParent.destroy() # otherwise destroy this container & a label if we have one else: parent.grid_forget() parent.destroy() try: self.widgetManager.remove(WIDGET_NAMES.Label, name) self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name) except: pass self.widgetManager.remove(WIDGET_NAMES.FrameBox, parent) else: gui.trace("Remove widget: %s", name) item.grid_forget() self.cleanseWidgets(item) ##################################### # FUNCTION for managing commands ##################################### @staticmethod def MAKE_FUNC(funcName, param): ''' function to automate lambdas ''' # make sure we get a function if not callable(funcName) and not hasattr(funcName, '__call__'): raise Exception("Invalid function: " + str(funcName)) # check if the function requires arguments argsList = getArgs(funcName) # if no args, or 1 arg in a bound function noArgs = len(argsList[0])==0 or (len(argsList[0])==1 and inspect.ismethod(funcName)) # if no args/varargs/kwargs then don't give the param if noArgs and argsList[1] is None and argsList[2] is None: return lambda *args: funcName() else: return lambda *args: funcName(param) def _checkFunc(self, names, funcs): singleFunc = None if funcs is None: return None elif callable(funcs): singleFunc = funcs elif len(names) != len(funcs): raise Exception("List sizes don't match") return singleFunc ##################################### # FUNCTIONS to position a widget ##################################### # properties for setting container's default rowspan/colspan def setColspan(self, colspan): self.containerStack[-1]['colspan'] = colspan def getColspan(self): return self.containerStack[-1]['colspan'] colspan = property(getColspan, setColspan) def setRowspan(self, rowspan): self.containerStack[-1]['rowspan'] = rowspan def getRowspan(self): return self.containerStack[-1]['rowspan'] rowspan = property(getRowspan, setRowspan) def getRow(self): return self._getContainerProperty('emptyRow') def gr(self): return self.getRow() def setRow(self, row): self.containerStack[-1]['emptyRow'] = row row = property(getRow, setRow) def _repackWidget(self, widget, params): if widget.winfo_manager() == "grid": ginfo = widget.grid_info() ginfo.update(params) widget.grid(ginfo) elif widget.winfo_manager() == "pack": pinfo = widget.pack_info() pinfo.update(params) widget.pack(pinfo) else: raise Exception("Unknown geometry manager: " + widget.winfo_manager()) # convenience function to set RCS, referencing the current container's # settings def _getRCS(self, row, column, colspan, rowspan): if row in[-1, 'previous', 'p', 'pr']: row = self._getContainerProperty('emptyRow') - 1 else: # this is the default, if row is None or row in ['next', 'n']: row = self._getContainerProperty('emptyRow') self.containerStack[-1]['emptyRow'] = row + 1 if column >= self._getContainerProperty('colCount'): self.containerStack[-1]['colCount'] = column + 1 # if column == 0 and colspan == 0 and self._getContainerProperty('colCount') > 1: # colspan = self._getContainerProperty('colCount') return row, column, colspan, rowspan @staticmethod def GET_WIDGET_CLASS(widget): return widget.__class__.__name__ @staticmethod def SET_WIDGET_FG(widget, fg, external=False): widgType = gui.GET_WIDGET_CLASS(widget) gui.trace("SET_WIDGET_FG: %s - %s", widgType, fg) # only configure these widgets if external if widgType in ["Link", "Spinbox", "AjText", "AjScrolledText", "Button", "Entry", "AutoCompleteEntry"]: if external: try: #Â entry specific settings if not widget.showingDefault: widget.oldFg = fg widget.config(fg=fg) else: widget.oldFg = fg except: # other widgets widget.config(fg=fg) # handle flash labels elif widgType == "Label": widget.config(fg=fg) widget.origFg=fg try: widget.config(bg=widget.origBg) except: pass # not a flash label elif widgType == "OptionMenu": if external: widget.config(fg=fg) widget["menu"].config(fg=fg) # deal with generic groupers elif widgType in ["Frame", "LabelFrame", "PanedFrame", "Pane", "ajFrame"]: for child in widget.winfo_children(): gui.SET_WIDGET_FG(child, fg, external) # deal with specific containers elif widgType == "LabelBox": try: if not widget.isValidation: gui.SET_WIDGET_FG(widget.theLabel, fg, external) except Exception as e: gui.SET_WIDGET_FG(widget.theLabel, fg, external) gui.SET_WIDGET_FG(widget.theWidget, fg, external) elif widgType == "ButtonBox": gui.SET_WIDGET_FG(widget.theWidget, fg, external) gui.SET_WIDGET_FG(widget.theButton, fg, external) elif widgType == "WidgetBox": for child in widget.theWidgets: gui.SET_WIDGET_FG(child, fg, external) elif widgType == "ListBoxContainer": if external: gui.SET_WIDGET_FG(widget.lb, fg, external) # skip these widgets elif widgType in ["PieChart", "MicroBitSimulator", "Scrollbar"]: pass # always try these widgets else: try: widget.config(fg=fg) except Exception as e: pass @staticmethod def TINT(widget, colour): col = [] for a, b in enumerate(widget.winfo_rgb(colour)): t = int(min(max(0, b / 256 + (255 - b / 256) * .3), 255)) t = str(hex(t))[2:] if len(t) == 1: t = '0' + t elif len(t) == 0: t = '00' col.append(t) if int(col[0], 16) > 210 and int(col[1], 16) > 210 and int(col[2], 16) > 210: if gui.GET_PLATFORM() == gui.LINUX: return "#c3c3c3" else: return "systemHighlight" else: return "#" + "".join(col) # convenience method to set a widget's bg @staticmethod def SET_WIDGET_BG(widget, bg, external=False, tint=False): if bg is None: # ignore empty colours return widgType = gui.GET_WIDGET_CLASS(widget) isDarwin = gui.GET_PLATFORM() == gui.MAC isLinux = gui.GET_PLATFORM() == gui.LINUX gui.trace("Config %s BG to %s", widgType, bg) # these have a highlight border to remove hideBorders = [ "Text", "AjText", "ScrolledText", "AjScrolledText", "Scale", "AjScale", "OptionMenu", "Entry", "AutoCompleteEntry", "Radiobutton", "Checkbutton", "Button"] # these shouldn't have their BG coloured by default noBg = [ "Button", "Scale", "AjScale", "Spinbox", "Listbox", "OptionMenu", "SplitMeter", "DualMeter", "Meter", "Entry", "AutoCompleteEntry", "Text", "AjText", "ScrolledText", "AjScrolledText", "ToggleFrame"] # remove the highlight borders if widgType in hideBorders: if widgType == "Entry" and widget.isValidation: pass elif widgType == "OptionMenu": widget["menu"].config(borderwidth=0) widget.config(highlightbackground=bg) if isDarwin: widget.config(background=bg) elif widgType in ["Radiobutton", "Checkbutton"]: widget.config(activebackground=bg, highlightbackground=bg) else: widget.config(highlightbackground=bg) # do some fancy tinting if external or tint: if widgType in ["Button", "Scale", "AjScale"]: widget.config(activebackground=gui.TINT(widget, bg)) elif widgType in ["Entry", "Text", "AjText", "ScrolledText", "AjScrolledText", "AutoCompleteEntry", "Spinbox"]: widget.config(selectbackground=gui.TINT(widget, bg)) widget.config(highlightcolor=gui.TINT(widget, bg)) if widgType in ["Text", "AjText", "ScrolledText", "AjScrolledText"]: widget.config(inactiveselectbackground=gui.TINT(widget, bg)) elif widgType == "Spinbox": widget.config(buttonbackground=bg) elif widgType == "Listbox": widget.config(selectbackground=gui.TINT(widget, bg)) elif widgType == "OptionMenu": widget.config(activebackground=gui.TINT(widget, bg)) widget["menu"].config(activebackground=gui.TINT(widget, bg)) elif widgType in ["Radiobutton", "Checkbutton"]: widget.config(activebackground=gui.TINT(widget, bg)) # if this is forced - change everything if external: widget.config(bg=bg) if widgType == "OptionMenu": widget["menu"].config(bg=bg) # otherwise only colour un-excluded widgets elif widgType not in noBg: widget.config(bg=bg) # deal with flash labels if widgType == "Label": widget.origBg=bg try: widget.config(fg=widget.origFg) except: pass # not a flash label # now do any of the below containers if widgType in ["LabelFrame", "PanedFrame", "Pane", "ajFrame"]: for child in widget.winfo_children(): gui.SET_WIDGET_BG(child, bg, external, tint) elif widgType == "LabelBox": # widget with label, in frame if widget.theLabel is not None: gui.SET_WIDGET_BG(widget.theLabel, bg, external, tint) gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint) elif widgType == "ButtonBox": # widget with button, in frame gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint) gui.SET_WIDGET_BG(widget.theButton, bg, external, tint) elif widgType == "ListBoxContainer": #Â list box container gui.SET_WIDGET_BG(widget.lb, bg, external, tint) elif widgType == "WidgetBox": # group of buttons or labels for widg in widget.theWidgets: gui.SET_WIDGET_BG(widg, bg, external, tint) def _getContainerProperty(self, prop=None): if prop is not None: return self.containerStack[-1][prop] else: return self.containerStack[-1] def _getContainerBg(self): if not self.ttkFlag: return self.getContainer()["bg"] else: return None def _getContainerFg(self): try: return self._getContainerProperty('fg') except: return "#000000" # two important things here: # grid - sticky: position of widget in its space (side or fill) # row/columns configure - weight: how to grow with GUI def _positionWidget( self, widget, row, column=0, colspan=0, rowspan=0, sticky=W+E, updateBg=True): # allow item to be added to container container = self.getContainer() if updateBg and not self.ttkFlag: gui.SET_WIDGET_FG(widget, self._getContainerFg()) gui.SET_WIDGET_BG(widget, self._getContainerBg()) # alpha paned window placement if self._getContainerProperty('type') == WIDGET_NAMES.PanedFrame: container.add(widget) self.containerStack[-1]['widgets'] = True return # else, add to grid row, column, colspan, rowspan = self._getRCS(row, column, colspan, rowspan) # build a dictionary for the named params iX = self._getContainerProperty('ipadx') iY = self._getContainerProperty('ipady') cX = self._getContainerProperty('padx') cY = self._getContainerProperty('pady') params = { "row": row, "column": column, "ipadx": iX, "ipady": iY, "padx": cX, "pady": cY} # sort out rowspan & colspan cColspan = self._getContainerProperty("colspan") cRowspan = self._getContainerProperty("rowspan") if colspan != 0: params["columnspan"] = colspan elif cColspan != 0: params["columnspan"] = cColspan if rowspan != 0: params["rowspan"] = rowspan elif cRowspan != 0: params["rowspan"] = cRowspan # 1) if param has sticky, use that # 2) if container has sticky - override # 3) else, none if self._getContainerProperty("sticky") is not None: params["sticky"] = self._getContainerProperty("sticky") elif sticky is not None: params["sticky"] = sticky else: pass # make colspanned widgets expand to fill height of cell if rowspan != 0: if "sticky" in params: if "n" not in params["sticky"]: params["sticky"] += "n" if "s" not in params["sticky"]: params["sticky"] += "s" else: params["sticky"] = "ns" # expand that dictionary out as we pass it as a value widget.grid(**params) self.containerStack[-1]['widgets'] = True # if we're in a PANEDFRAME - we need to set parent... if self._getContainerProperty('type') == WIDGET_NAMES.Pane: self.containerStack[-2]['widgets'] = True # configure the row/column to expand equally if self._getContainerProperty('expand') in ["ALL", "COLUMN"]: Grid.columnconfigure(container, column, weight=1) else: Grid.columnconfigure(container, column, weight=0) if self._getContainerProperty('expand') in ["ALL", "ROW"]: Grid.rowconfigure(container, row, weight=1) else: Grid.rowconfigure(container, row, weight=0) # self._getContainerProperty('container').columnconfigure(0, weight=1) # self._getContainerProperty('container').rowconfigure(0, weight=1) ##################################### # FUNCTION to manage containers ##################################### # prepares a new empty container dict def _prepContainer(self, cTitle, cType, container, row, col, sticky=None): containerData = {'type': cType, 'title': cTitle, 'container': container, 'emptyRow': row, 'colCount': col, 'sticky': sticky, 'padx': 0, 'pady': 0, 'ipadx': 0, 'ipady': 0, 'expand': "ALL", 'widgets': False, 'inputFont': self._inputFont, 'labelFont': self._labelFont, 'buttonFont': self._buttonFont, "fg": self._getContainerFg(), "colspan":0, "rowspan":0, } return containerData # adds the container to the container stack - makes this the current working container def _addContainer(self, cTitle, cType, container, row, col, sticky=None): containerData = self._prepContainer(cTitle, cType, container, row, col, sticky) self.containerStack.append(containerData) def openFrameStack(self, title): return self._openContainer(WIDGET_NAMES.FrameStack, title) def openSubFrame(self, frameTitle, frameNumber): return self._openContainer(WIDGET_NAMES.SubFrame, frameTitle+"__"+str(frameNumber)) def openRootPage(self, title): return self._openContainer(WIDGET_NAMES.RootPage, title) def openLabelFrame(self, title): return self._openContainer(WIDGET_NAMES.LabelFrame, title) def openFrame(self, title): try: return self._openContainer(WIDGET_NAMES.Frame, title) except: return self._openContainer(WIDGET_NAMES.SubFrame, title) def openToggleFrame(self, title): return self._openContainer(WIDGET_NAMES.ToggleFrame, title) def openPagedWindow(self, title): return self._openContainer(WIDGET_NAMES.PagedWindow, title) def openPage(self, windowTitle, pageNumber): return self._openContainer(WIDGET_NAMES.Page, windowTitle+"__"+str(pageNumber)) def openTabbedFrame(self, title): return self._openContainer(WIDGET_NAMES.TabbedFrame, title) def openTab(self, frameTitle, tabTitle): return self._openContainer(WIDGET_NAMES.Tab, frameTitle+"__"+tabTitle) def openNotebook(self, title): return self._openContainer(WIDGET_NAMES.Notebook, title) def openNote(self, frameTitle, tabTitle): return self._openContainer(WIDGET_NAMES.Notebook, frameTitle+"__"+tabTitle) def openPanedFrame(self, title): return self._openContainer(WIDGET_NAMES.PanedFrame, title) def openPane(self, title): return self._openContainer(WIDGET_NAMES.Pane, title) def openSubWindow(self, title): return self._openContainer(WIDGET_NAMES.SubWindow, title) def openScrollPane(self, title): return self._openContainer(WIDGET_NAMES.ScrollPane, title) # function to reload the specified container def _openContainer(self, kind, title): # get the cached container config for this container kind = WIDGET_NAMES.name(kind) cName = kind + "__" + title try: cConf = self.widgetManager.get(WIDGET_NAMES.ContainerLog, cName) except KeyError: raise Exception("Attempted to open invalid " + kind + ": " + str(title)) self.containerStack.append(cConf) return cConf['container'] # returns the current working container def getContainer(self): container = self._getContainerProperty('container') if self._getContainerProperty('type') == WIDGET_NAMES.ScrollPane: return container.interior elif self._getContainerProperty('type') == WIDGET_NAMES.PagedWindow: return container.getPage() elif self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame: return container.getContainer() elif self._getContainerProperty('type') == WIDGET_NAMES.SubWindow: return container.canvasPane else: return container # if possible, removes the current container def _removeContainer(self): if len(self.containerStack) == 1: raise Exception("Can't remove container, already in root window.") else: container = self.containerStack.pop() if not container['widgets']: self.warn("Closing empty container: %s", container['title']) # store the container so that it can be re-opened later name = WIDGET_NAMES.name(container["type"]) + "__" + container["title"] try: self.widgetManager.add(WIDGET_NAMES.ContainerLog, name, container) except: pass #Â we'll ignore, as that means we already added it... return container # functions to start the various containers def startContainer(self, fType, title, row=None, column=0, colspan=0, rowspan=0, sticky=None, name=None): if name is None: name = title if fType == WIDGET_NAMES.LabelFrame: # first, make a LabelFrame, and position it correctly self.widgetManager.verify(WIDGET_NAMES.LabelFrame, title) if not self.ttkFlag: container = LabelFrame(self.getContainer(), text=name, relief="groove") container.config(background=self._getContainerBg(), font=self._getContainerProperty('labelFont')) else: container = ttk.LabelFrame(self.getContainer(), text=name, relief="groove") container.DEFAULT_TEXT = name container.isContainer = True self.setPadX(5) self.setPadY(5) self._positionWidget(container, row, column, colspan, rowspan, "nsew") self.widgetManager.add(WIDGET_NAMES.LabelFrame, title, container) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.LabelFrame, container, 0, 1, sticky) return container elif fType == WIDGET_NAMES.Canvas: # first, make a canvas, and position it correctly self.widgetManager.verify(WIDGET_NAMES.Canvas, title) container = Canvas(self.getContainer()) container.isContainer = True self._positionWidget(container, row, column, colspan, rowspan, "nsew") self.widgetManager.add(WIDGET_NAMES.Canvas, title, container) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Canvas, container, 0, 1, "") return container elif fType == WIDGET_NAMES.TabbedFrame: self.widgetManager.verify(WIDGET_NAMES.TabbedFrame, title) tabbedFrame = self._tabbedFrameMaker(self.getContainer(), self.ttkFlag, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: tabbedFrame.config(bg=self._getContainerBg()) # tabbedFrame.isContainer = True self._positionWidget( tabbedFrame, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.TabbedFrame, title, tabbedFrame) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.TabbedFrame, tabbedFrame, 0, 1, sticky) return tabbedFrame elif fType == WIDGET_NAMES.Tab: # add to top of stack self.containerStack[-1]['widgets'] = True tabTitle = self._getContainerProperty('title') + "__" + title tab = self._getContainerProperty('container').addTab(title) self._addContainer(tabTitle, WIDGET_NAMES.Tab, tab, 0, 1, sticky) return tab elif fType == WIDGET_NAMES.Notebook: if not self.ttkFlag: raise Exception("Cannot create a ttk Notebook, unless ttk is enabled.") self.widgetManager.verify(WIDGET_NAMES.Notebook, title) notebook = ttk.Notebook(self.getContainer()) # tabbedFrame.isContainer = True self._positionWidget( notebook, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.Notebook, title, notebook) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Notebook, notebook, 0, 1, sticky) return notebook elif fType == WIDGET_NAMES.Note: # add to top of stack self.containerStack[-1]['widgets'] = True noteTitle = self._getContainerProperty('title') + "__" + title frame = ttk.Frame(self._getContainerProperty('container')) self._getContainerProperty('container').add(frame, text=title) self._addContainer(noteTitle, WIDGET_NAMES.Note, frame, 0, 1, sticky) return frame elif fType == WIDGET_NAMES.PanedFrame: # if we previously put a frame for widgets # remove it if self._getContainerProperty('type') == WIDGET_NAMES.Pane: self.stopContainer() # now, add the new pane self.widgetManager.verify(WIDGET_NAMES.PanedFrame, title) pane = PanedWindow( self.getContainer(), showhandle=True, sashrelief="groove", bg=self._getContainerBg()) pane.isContainer = True self._positionWidget( pane, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.PanedFrame, title, pane) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.PanedFrame, pane, 0, 1, sticky) # now, add a frame to the pane self.startContainer(WIDGET_NAMES.Pane, title) return pane elif fType == WIDGET_NAMES.Pane: # create a frame, and add it to the pane pane = Pane(self.getContainer(), bg=self._getContainerBg()) pane.isContainer = True self._getContainerProperty('container').add(pane) self.widgetManager.add(WIDGET_NAMES.Pane, title, pane) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Pane, pane, 0, 1, sticky) return pane elif fType == WIDGET_NAMES.ScrollPane: self.widgetManager.verify(WIDGET_NAMES.ScrollPane, title) # naned used to diabled sctollbars if name not in ["horizontal", "vertical", ""]: gui.warn("ScrollPane %s: Invalid value for disabled, must be one of 'horizontal' or 'vertical'", title) scrollPane = ScrollPane(self.getContainer(), disabled=name) if not self.ttkFlag: scrollPane.config(bg=self._getContainerBg()) scrollPane.isContainer = True self._positionWidget( scrollPane, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.ScrollPane, title, scrollPane) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.ScrollPane, scrollPane, 0, 1, sticky) return scrollPane elif fType == WIDGET_NAMES.ToggleFrame: self.widgetManager.verify(WIDGET_NAMES.ToggleFrame, title) toggleFrame = ToggleFrame(self.getContainer(), title=title, bg=self._getContainerBg()) toggleFrame.configure(font=self._getContainerProperty('labelFont')) toggleFrame.isContainer = True self._positionWidget( toggleFrame, row, column, colspan, rowspan, sticky=sticky) self._addContainer(title, WIDGET_NAMES.ToggleFrame, toggleFrame, 0, 1, "nw") self.widgetManager.add(WIDGET_NAMES.ToggleFrame, title, toggleFrame) return toggleFrame elif fType == WIDGET_NAMES.PagedWindow: # create the paged window pagedWindow = PagedWindow(self.getContainer(), title=title, bg=self._getContainerBg(), width=200, height=400, buttonFont=self._getContainerProperty('buttonFont'), titleFont=self._getContainerProperty('labelFont')) # bind events self.topLevel.bind("<Left>", pagedWindow.showPrev) self.topLevel.bind("<Control-Left>", pagedWindow.showFirst) self.topLevel.bind("<Right>", pagedWindow.showNext) self.topLevel.bind("<Control-Right>", pagedWindow.showLast) # register it as a container pagedWindow.isContainer = True self._positionWidget(pagedWindow, row, column, colspan, rowspan, sticky=sticky) self._addContainer(title, WIDGET_NAMES.PagedWindow, pagedWindow, 0, 1, "nw") self.widgetManager.add(WIDGET_NAMES.PagedWindow, title, pagedWindow) return pagedWindow elif fType == WIDGET_NAMES.Page: page = self._getContainerProperty('container').addPage() page.isContainer = True self._addContainer(title, WIDGET_NAMES.Page, page, 0, 1, sticky) self.containerStack[-1]['expand'] = "None" return page elif fType == WIDGET_NAMES.FrameStack: # create the paged window frameStack = FrameStack(self.getContainer(), bg=self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.FrameStack, title, frameStack) # register it as a container frameStack.isContainer = True self._positionWidget(frameStack, row, column, colspan, rowspan, sticky=sticky) self._addContainer(title, WIDGET_NAMES.FrameStack, frameStack, 0, 1, "news") return frameStack elif fType == WIDGET_NAMES.Frame: # first, make a Frame, and position it correctly self.widgetManager.verify(WIDGET_NAMES.Frame, title) container = self._makeAjFrame()(self.getContainer()) container.isContainer = True container.config(background=self._getContainerBg()) self._positionWidget( container, row, column, colspan, rowspan, "nsew") self.widgetManager.add(WIDGET_NAMES.Frame, title, container) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Frame, container, 0, 1, sticky) return container elif fType == WIDGET_NAMES.SubFrame: subFrame = self._getContainerProperty('container').addFrame() subFrame.isContainer = True self._addContainer(title, WIDGET_NAMES.SubFrame, subFrame, 0, 1, "news") self.widgetManager.add(WIDGET_NAMES.Frame, title, subFrame) return subFrame else: raise Exception("Unknown container: " + fType) ##################################### # Notebooks ##################################### @contextmanager def notebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): try: note = self.startNotebook(title, row, column, colspan, rowspan, sticky) except ItemLookupError: note = self.openNotebook(title) self.configure(**kwargs) try: yield note finally: self.stopNotebook() def startNotebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): return self.startContainer(WIDGET_NAMES.Notebook, title, row, column, colspan, rowspan, sticky) def stopNotebook(self): # auto close the existing TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Note: self.warn("You didn't STOP the previous NOTE") self.stopContainer() self.stopContainer() @contextmanager def note(self, title, tabTitle=None, **kwargs): if tabTitle is None: note = self.startNote(title) else: note = self.openNote(title, tabTitle) self.configure(**kwargs) try: yield note finally: self.stopNote() def startNote(self, title): # auto close the previous TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Note: self.warn("You didn't STOP the previous NOTE") self.stopContainer() elif self._getContainerProperty('type') != WIDGET_NAMES.Notebook: raise Exception( "Can't add a Note to the current container: ", self._getContainerProperty('type')) return self.startContainer(WIDGET_NAMES.Note, title) def stopNote(self): if self._getContainerProperty('type') != WIDGET_NAMES.Note: raise Exception("Can't stop a NOTE, currently in:", self._getContainerProperty('type')) self.stopContainer() """ def startCanvas(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="news"): return self.startContainer(WIDGET_NAMES.Canvas, title) def stopCanvas(self): if self._getContainerProperty('type') != WIDGET_NAMES.Canvas: raise Exception("Can't stop a CANVAS, currently in:", self._getContainerProperty('type')) self.stopContainer() @contextmanager def canvas(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): try: canvas = self.startCanvas(title, row, column, colspan, rowspan, sticky) except ItemLookupError: canvas = self.openCanvas(title) try: yield canvas finally: self.stopCanvas() """ ##################################### # Tabbed Frames ##################################### ################################# # TabbedFrame Class ################################# def _tabbedFrameMaker(self, master, useTtk=False, **kwargs): global OrderedDict if OrderedDict is None: from collections import OrderedDict class TabBorder(Frame, object): def __init__(self, master, **kwargs): super(TabBorder, self).__init__(master, **kwargs) self.config(borderwidth=0, highlightthickness=0, bg='darkGray') class TabContainer(frameBase, object): def __init__(self, master, **kwargs): super(TabContainer, self).__init__(master, **kwargs) TabBorder(self, height=2).pack(side=TOP, expand=True, fill=X) TabBorder(self, width=2).pack(side=LEFT, fill=Y, expand=0) class TabText(labelBase, object): def __init__(self, master, func, text, **kwargs): super(TabText, self).__init__(master, text=text, **kwargs) self.disabled = False self.DEFAULT_TEXT = text self.hidden = False self.bind("<Button-1>", lambda *args: func(text)) self.border = TabBorder(master, width=2) def rename(self, newName): # use the DEFAULT_TEXT if necessary if newName is None: newName = self.DEFAULT_TEXT self.config(text=newName) def hide(self): self.hidden = True self.border.pack_forget() self.pack_forget() def display(self, fill=False, beforeTab=None, afterTab=None): self.border.pack_forget() self.pack_forget() if not self.hidden: if fill: self.pack(side=LEFT, ipady=4, ipadx=4, expand=True, fill=BOTH, before=beforeTab, after=afterTab) else: self.pack(side=LEFT, ipady=4, ipadx=4, before=beforeTab, after=afterTab) self.border.pack(side=LEFT, fill=Y, expand=0, before=beforeTab, after=afterTab) class TabbedFrame(frameBase, object): def __init__(self, master, fill=False, changeOnFocus=True, font=None, **kwargs): # main frame & tabContainer inherit BG colour super(TabbedFrame, self).__init__(master, **kwargs) self.fill = fill self.selectedTab = None self.changeOnFocus = changeOnFocus self.changeEvent = None self.beforeTab = None self.afterTab = None # layout the grid Grid.columnconfigure(self, 0, weight=1) Grid.rowconfigure(self, 1, weight=1) # create two containers self.tabContainer = TabContainer(self, **kwargs) self.panes = FrameStack(self) self.panes.SKIP_CLEANSE = True # now grid minimised or stretched if self.fill: self.tabContainer.grid(row=0, sticky=W + E) else: self.tabContainer.grid(row=0, sticky=W) self.panes.grid(row=1, sticky="NESW") self.EMPTY_PANE = self.panes.addFrame() # nain store dictionary: name = [tab, pane] self.widgetStore = OrderedDict() # looks self.tabFont = font if gui.GET_PLATFORM() == gui.MAC: self.inactiveCursor="pointinghand" elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.inactiveCursor="hand2" # selected tab & all panes self.activeFg = "#000000" self.activeBg = "#F6F6F6" # other tabs self.inactiveFg = "#000000" self.inactiveBg = "#DADADA" # disabled tabs self.disabledFg = "gray" self.disabledBg = "darkGray" if useTtk: self.ttkStyle = ttk.Style() self.ttkStyle.configure("ActiveTab.TLabel", foreground=self.activeFg, background=self.activeBg) self.ttkStyle.configure("InactiveTab.TLabel", foreground=self.inactiveFg, background=self.inactiveBg) self.ttkStyle.configure("DisabledTab.TLabel", foreground=self.disabledFg, background=self.disabledBg) self.ttkStyle.configure("DisabledTab.TFrame", background=self.disabledBg) self.EMPTY_PANE.config(style="DisabledTab.TFrame") else: self.EMPTY_PANE.config(bg=self.disabledBg) def config(self, cnf=None, **kw): self.configure(cnf, **kw) def setBeforeTab(self, tab=None): if tab is not None: self.beforeTab = self.widgetStore[tab][0] else: self.beforeTab = None def setAfterTab(self, tab=None): if tab is not None: self.afterTab = self.widgetStore[tab][0] else: self.afterTab = None def configure(self, cnf=None, **kw): kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) if "activeforeground" in kw: self.activeFg = kw.pop("activeforeground") if "activebackground" in kw: self.activeBg = kw.pop("activebackground") if "fg" in kw: self.inactiveFg = kw.pop("fg") if "inactivebackground" in kw: self.inactiveBg = kw.pop("inactivebackground") if "inactiveforeground" in kw: self.inactiveFg = kw.pop("inactiveforeground") if "disabledforeground" in kw: self.disabledFg = kw.pop("disabledforeground") if "disabledbackground" in kw: self.disabledBg = kw.pop("disabledbackground") if "bg" in kw: self.tabContainer.configure(bg=kw["bg"]) if "font" in kw: self.tabFont.config(kw.pop("font")) if "command" in kw: self.changeEvent = kw.pop("command") # just in case if not useTtk: self.EMPTY_PANE.config(bg=self.disabledBg) else: self.ttkStyle.configure("ActiveTab.TLabel", foreground=self.activeFg, background=self.activeBg) self.ttkStyle.configure("InactiveTab.TLabel", foreground=self.inactiveFg, background=self.inactiveBg) self.ttkStyle.configure("DisabledTab.TLabel", foreground=self.disabledFg, background=self.disabledBg) self.ttkStyle.configure("DisabledTab.TFrame", background=self.disabledBg) # update tabs if we have any self._configTabs() # propagate any left over confs super(TabbedFrame, self).config(cnf, **kw) def hideTab(self, title): if title not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + title) self.widgetStore[title][0].hide() if self.selectedTab == title: self.selectedTab = None self._findNewTab() self._configTabs() def deleteTab(self, title): self.hideTab(title) tab = self.widgetStore[title][0] tab.border.destroy() tab.destroy() pane = self.widgetStore[title][1] pane.grid_forget() pane.destroy() del self.widgetStore[title] def showTab(self, title): if title not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + title) self.widgetStore[title][0].hidden = False self.expandTabs(self.fill) if self.selectedTab == None: self.changeTab(title) def disableAllTabs(self, disabled=True): for tab in self.widgetStore.keys(): self.disableTab(tab, disabled, refresh=False) self._configTabs() if disabled: self.selectedTab = None self.EMPTY_PANE.lift() def disableTab(self, tabName, disabled=True, refresh=True): if tabName not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + tabName) tab = self.widgetStore[tabName][0] tab.disabled = disabled if not disabled and not tab.hidden and self.selectedTab is None: self.selectedTab = tabName elif disabled and self.selectedTab == tabName: self.selectedTab = None if refresh: self._findNewTab() if refresh: self._configTabs() def addTab(self, text, **kwargs): # check for duplicates if text in self.widgetStore: raise ItemLookupError("Duplicate tabName: " + text) tab = TabText(self.tabContainer, text=text, func=self.changeTab, font=self.tabFont, **kwargs) tab.display(self.fill, beforeTab=self.beforeTab, afterTab=self.afterTab) # create the pane pane = self.panes.addFrame() if not useTtk: pane.config(bg=self.activeBg) # log the first tab as the selected tab if self.selectedTab is None: self.selectedTab = text # log the widgets self.widgetStore[text] = [tab, pane] self._configTabs() return pane def getTab(self, title): if title not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + title) else: return self.widgetStore[title][1] def expandTabs(self, fill=True): self.fill = fill # update the tabConatiner self.tabContainer.grid_forget() if self.fill: self.tabContainer.grid(row=0, sticky=W + E) else: self.tabContainer.grid(row=0, sticky=W) for key in list(self.widgetStore.keys()): tab = self.widgetStore[key][0] tab.display(self.fill) def renameTab(self, tabName, newName=None): if tabName not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + tabName) self.widgetStore[tabName][0].rename(newName) def changeTab(self, tabName, callFunction=True): if tabName not in self.widgetStore.keys(): raise ItemLookupError("Invalid tab name: " + tabName) # stop if already selected or disabled if self.selectedTab == tabName or self.widgetStore[tabName][0].disabled or self.widgetStore[tabName][0].hidden: return self.selectedTab = tabName self._configTabs() if self.changeEvent is not None and callFunction: self.changeEvent(tabName) def getSelectedTab(self): return self.selectedTab def setFont(self, **kwargs): self.tabFont.config(**kwargs) def _findNewTab(self): for key in list(self.widgetStore.keys()): if not self.widgetStore[key][0].disabled and not self.widgetStore[key][0].hidden: self.changeTab(key) return # if we're here - all tabs are disabled self.selectedTab = None self.EMPTY_PANE.lift() def _configTabs(self): for key in list(self.widgetStore.keys()): if self.widgetStore[key][0].disabled: if not useTtk: self.widgetStore[key][0].config(bg=self.disabledBg, fg=self.disabledFg, cursor="") else: self.widgetStore[key][0].config(style="DisabledTab.TLabel", cursor="") else: if key == self.selectedTab: if not useTtk: self.widgetStore[key][0].config(bg=self.widgetStore[key][1].cget('bg'), fg=self.activeFg, cursor="") else: self.widgetStore[key][0].config(style="SelectedTab.TLabel", cursor="") self.widgetStore[key][1].lift() else: if not useTtk: self.widgetStore[key][0].config(bg=self.inactiveBg, fg=self.inactiveFg, cursor=self.inactiveCursor) else: self.widgetStore[key][0].config(style="InactiveTab.TLabel", cursor=self.inactiveCursor) return TabbedFrame(master, **kwargs) @contextmanager def tabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): try: tabs = self.startTabbedFrame(title, row, column, colspan, rowspan, sticky) except ItemLookupError: tabs = self.openTabbedFrame(title) command = kwargs.pop("change", None) if command is not None: self.setTabbedFrameChangeCommand(title, command) self.configure(**kwargs) try: yield tabs finally: self.stopTabbedFrame() def startTabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): return self.startContainer(WIDGET_NAMES.TabbedFrame, title, row, column, colspan, rowspan, sticky) def stopTabbedFrame(self): # auto close the existing TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Tab: self.warn("You didn't STOP the previous TAB") self.stopContainer() self.stopContainer() def setTabbedFrameChangeCommand(self, title, func): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) command = self.MAKE_FUNC(func, title) nb.config(command=command) def setTabbedFrameTabExpand(self, title, expand=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.expandTabs(expand) def setTabbedFrameSelectedTab(self, title, tab, callFunction=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) try: nb.changeTab(tab, callFunction) except KeyError: raise ItemLookupError("Invalid tab name: " + str(tab)) def setTabbedFrameDisabledTab(self, title, tab, disabled=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.disableTab(tab, disabled) def setTabbedFrameDisableAllTabs(self, title, disabled=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.disableAllTabs(disabled) def deleteTabbedFrameTab(self, title, tab): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) self.cleanseWidgets(nb.getTab(tab)) nb.deleteTab(tab) def showTabbedFrameTab(self, title, tab): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.showTab(tab) def hideTabbedFrameTab(self, title, tab): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.hideTab(tab) def setTabText(self, title, tab, newText=None): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.renameTab(tab, newText) def setTabFont(self, title, **kwargs): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.setFont(**kwargs) def setTabBg(self, title, tab, colour): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) tab = nb.getTab(tab) gui.SET_WIDGET_BG(tab, colour) # tab.config(bg=colour) #gui.SET_WIDGET_BG(tab, colour) for child in tab.winfo_children(): gui.SET_WIDGET_BG(child, colour) @contextmanager def tab(self, title, tabTitle=None, **kwargs): beforeTab = kwargs.pop("beforeTab", None) afterTab = kwargs.pop("afterTab", None) if tabTitle is None: try: tab = self.startTab(title, beforeTab, afterTab) except ItemLookupError: if self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame: raise Exception("Can't open a Tab in the current container: ", self._getContainerProperty('type')) else: tabTitle = self._getContainerProperty('title') tab = self.openTab(tabTitle, title) else: tab = self.openTab(title, tabTitle) self.configure(**kwargs) try: yield tab finally: self.stopTab() def startTab(self, title, beforeTab=None, afterTab=None): if beforeTab is not None and afterTab is not None: self.warn("You can't specify a before and after value for tab: %s", title) beforeTab = afterTab = None # auto close the previous TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Tab: self.warn("You didn't STOP the previous TAB") self.stopContainer() elif self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame: raise Exception("Can't add a Tab to the current container: ", self._getContainerProperty('type')) tf = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, self._getContainerProperty("title")) tf.setBeforeTab(beforeTab) tf.setAfterTab(afterTab) tab = self.startContainer(WIDGET_NAMES.Tab, title) tf.setBeforeTab() tf.setAfterTab() return tab def getTabbedFrameSelectedTab(self, title): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) return nb.getSelectedTab() def stopTab(self): if self._getContainerProperty('type') != WIDGET_NAMES.Tab: raise Exception("Can't stop a TAB, currently in:", self._getContainerProperty('type')) self.stopContainer() ##################################### # Simple Tables ##################################### def _getDbTables(self, db): ''' query the specified database, and get a list of table names ''' self._importSqlite3() if not sqlite3: self.error("Unable to load DB tables - can't load sqlite3") return [] query = "SELECT DISTINCT tbl_name FROM sqlite_master ORDER BY tbl_name COLLATE NOCASE" data = [] with sqlite3.connect(db) as conn: cursor = conn.cursor() cursor.execute(query) for row in cursor: data.append(row[0]) return data def replaceDbTable(self, title, db, table): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.db = db grid.dbTable = table self._importSqlite3() if not sqlite3: self.error("Unable to load DB data - can't load sqlite3") return with sqlite3.connect(db) as conn: cursor = conn.cursor() dataQuery = 'SELECT * from ' + table # select all data cursor.execute(dataQuery) self.setTableHeaders(title, cursor) self.replaceAllTableRows(title, cursor) self.topLevel.update_idletasks() def disableTableEntry(self, title, entryPos, disabled=True): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.disableEntry(entryPos, disabled=disabled) def refreshDbTable(self, title): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) self._importSqlite3() if not sqlite3: self.error("Unable to load DB data - can't load sqlite3") return with sqlite3.connect(grid.db) as conn: cursor = conn.cursor() dataQuery = 'SELECT * from ' + grid.dbTable # select all data cursor.execute(dataQuery) self.replaceAllTableRows(title, cursor) def refreshDbOptionBox(self, title, selected=None): opt = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) data = self._getDbTables(opt.db) self.changeOptionBox(title, data) if selected is not None: self.setOptionBox(title, selected) def table(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets tables all in one go """ widgKind = WIDGET_NAMES.Table kind = kwargs.pop("kind", 'normal') action=kwargs.pop('action', None) addRow=kwargs.pop('addRow', None) actionHeading=kwargs.pop('actionHeading', "Action") actionButton=kwargs.pop('actionButton', "Press") addButton=kwargs.pop('addButton', "Add") showMenu=kwargs.pop('showMenu', False) horiz=kwargs.pop('horizontal', True) change=kwargs.pop('change', None) edit=kwargs.pop('edit', None) try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: self.replaceAllTableRows(title, value) table = self.getTableEntries(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == 'normal': table = self.addTable(title, value, *args, action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton, addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs ) else: table = self.addDbTable(title, value, *args, action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton, addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs ) if change is not None: self.setTableChangeFunction(title, change) if edit is not None: self.setTableEditFunction(title, edit) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return table def addDbTable(self, title, value, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False, border="solid", **kwargs): ''' creates a new Table, displaying the specified database & table ''' horiz=kwargs.pop('horizontal', True) self._importSqlite3() if not sqlite3: self.error("Unable to load DB data - can't load sqlite3") return with sqlite3.connect(value) as conn: cursor = conn.cursor() dataQuery = 'SELECT * from ' + table # select all data cursor.execute(dataQuery) grid = self.addTable(title, cursor, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu, border=border, horizontal=horiz ) grid.db = value grid.dbTable = table return grid def addTable(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False, border="solid", **kwargs): ''' creates a new table, displaying the specified data ''' self.widgetManager.verify(WIDGET_NAMES.Table, title) wrap=kwargs.pop('wrap', 250) horiz=kwargs.pop('horizontal', True) if not self.ttkFlag: grid = SimpleTable(self.getContainer(), title, data, action, addRow, actionHeading, actionButton, addButton, showMenu, buttonFont=self._getContainerProperty('buttonFont'), font=self.tableFont, background=self._getContainerBg(), queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz ) else: grid = SimpleTable(self.getContainer(), title, data, action, addRow, actionHeading, actionButton, addButton, showMenu, buttonFont=self._getContainerProperty('buttonFont'), queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz ) self._positionWidget(grid, row, column, colspan, rowspan, N+E+S+W) self.widgetManager.add(WIDGET_NAMES.Table, title, grid) return grid def getTableEntries(self, title): return self.widgetManager.get(WIDGET_NAMES.Table, title).getEntries() def getTableLastChange(self, title): return self.widgetManager.get(WIDGET_NAMES.Table, title).getLastChange() def getTableSelectedCells(self, title): return self.widgetManager.get(WIDGET_NAMES.Table, title).getSelectedCells() def selectTableRow(self, title, row, highlight=None): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.selectRow(row, highlight) def setTableEditFunction(self, title, func): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) cmd = self.MAKE_FUNC(func, title) grid.config(edit=cmd) def selectTableColumn(self, title, col, highlight=None): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.selectColumn(col, highlight) def addTableRow(self, title, data): ''' adds a new row of data to the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.addRow(data) def addTableRows(self, title, data): ''' adds multiple rows of data to the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.addRows(data, scroll=True) def addTableColumn(self, title, columnNumber, data): ''' adds a new column of data, in the specified position, to the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.addColumn(columnNumber, data) def deleteTableColumn(self, title, columnNumber): ''' deletes the specified column from the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteColumn(columnNumber) def setTableHeaders(self, title, data): ''' change the headers in the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.setHeaders(data) def deleteTableRow(self, title, rowNum): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteRow(rowNum) def deleteAllTableRows(self, title): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteAllRows() def sortTable(self, title, columnNumber, descending=False): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.sort(columnNumber, descending) def getTableRowCount(self, title): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) return grid.getRowCount() def getTableRow(self, title, rowNumber): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) return grid.getRow(rowNumber) def confTable(self, title, field, value): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) kw = {field:value} grid.config(**kw) def replaceTableRow(self, title, rowNum, data): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.replaceRow(rowNum, data) def replaceAllTableRows(self, title, data, deleteHeader=True): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteAllRows(deleteHeader=deleteHeader) grid.addRows(data, scroll=False) # temporary deprecated functions def addGrid(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False): ''' DEPRECATED - adds a new grid widget with the specified data ''' gui.warn("Deprecated - grids renamed to tables") return self.addTable(title, data, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu) def addDbGrid(self, title, db, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False): ''' DEPRECATED - adds a new table widget, with the specified database and table ''' gui.warn("Deprecated - grids renamed to tables") return self.addDbTable(title, db, table, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu) def replaceDbGrid(self, title, db, table): gui.warn("Deprecated - grids renamed to tables") return self.replaceDbTable(title, db, table) def refreshDbGrid(self, title): gui.warn("Deprecated - grids renamed to tables") return self.refreshDbTable(title) def selectGridRow(self, title, row, highlight=None): gui.warn("Deprecated - grids renamed to tables") return self.selectTableRow(title, row, highlight) def getGridEntries(self, title): gui.warn("Deprecated - grids renamed to tables") return self.getTableEntries(title) def getGridSelectedCells(self, title): gui.warn("Deprecated - grids renamed to tables") return self.getTableSelectedCells(title) def selectGridColumn(self, title, col, highlight=None): return self.selectTableColumn(title, col, highlight) def addGridRow(self, title, data): ''' DEPRECATED - adds a row of data to the specified grid ''' return self.addTableRow(title, data) def addGridRows(self, title, data): ''' DEPRECATED - adds new rows of data to the specified grid ''' return self.addTableRows(title, data) def addGridColumn(self, title, columnNumber, data): ''' DEPRECATED - adds a column of data to the specified grid ''' return self.addTableColumn(title, columnNumber, data) def deleteGridColumn(self, title, columnNumber): return self.deleteTableColumn(title, columnNumber) def setGridHeaders(self, title, data): return self.setTableHeaders(title, data) def deleteGridRow(self, title, rowNum): return self.deleteTableRow(title, rowNum) def deleteAllGridRows(self, title): return self.deleteAllTableRows(title) def sortGrid(self, title, columnNumber, descending=False): return self.sortTable(title, columnNumber, descending) def getGridRowCount(self, title): return self.getTableRowCount(title) def getGridRow(self, title, rowNumber): return self.getTableRow(title, rowNumber) def confGrid(self, title, field, value): return self.confTable(title, field, value) def replaceGridRow(self, title, rowNum, data): return self.replaceTableRow(title, rowNum, data) def replaceAllGridRows(self, title, data): return self.replaceAllTableRows(title, data) ##################################### # Paned Frames ##################################### @contextmanager def panedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): vertical = kwargs.pop("vertical", False) sash = kwargs.pop("sash", 50) reOpen = False try: pane = self.startPanedFrame(title, row, column, colspan, rowspan, sticky) except ItemLookupError: reOpen = True pane = self.openPane(title) if vertical: self.setPanedFrameVertical(title) self.configure(**kwargs) try: yield pane finally: if reOpen: self.stopContainer() else: self.stopPanedFrame() self.setPaneSashPosition(sash, pane) @contextmanager def panedFrameVertical(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): gui.warn('Setting panedFrameVertical(%s) is deprecated, please use panedFrame(vertical=True)', title) reOpen = False sash = kwargs.pop("sash", 50) try: pane = self.startPanedFrameVertical(title, row, column, colspan, rowspan, sticky) except ItemLookupError: reOpen = True pane = self.openPane(title) self.configure(**kwargs) try: yield pane finally: if reOpen: self.stopContainer() else: self.stopPanedFrame() self.setPaneSashPosition(sash, pane) def startPanedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): p = self.startContainer(WIDGET_NAMES.PanedFrame, title, row, column, colspan, rowspan, sticky) return p def startPanedFrameVertical( self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): p = self.startPanedFrame(title, row, column, colspan, rowspan, sticky) self.setPanedFrameVertical(title) return p def stopPanedFrame(self): if self._getContainerProperty('type') == WIDGET_NAMES.Pane: self.stopContainer() if self._getContainerProperty('type') != WIDGET_NAMES.PanedFrame: raise Exception("Can't stop a PANEDFRAME, currently in:", self._getContainerProperty('type')) self.stopContainer() # make a PanedFrame align vertically def setPanedFrameVertical(self, window): pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, window) pane.config(orient=VERTICAL) def setPaneSashPosition(self, pos, pane=None): # convert to a percentage if needed if pos > 1: pos = pos / 100.0 if pane is None: if self._getContainerProperty('type') == WIDGET_NAMES.PanedFrame: pane = self._getContainerProperty('container') elif self.containerStack[-2]['type'] == WIDGET_NAMES.PanedFrame: pane = self.containerStack[-2]['container'] elif self._getContainerProperty('type') == WIDGET_NAMES.Pane: pane = self._getContainerProperty('container').parent else: gui.error("Unable to set sash position - can't find a pane") return elif type(pane) == str: pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, pane) if pane.cget('orient') == 'horizontal': w = int(pane.winfo_width() * pos) try: pane.sash_place(0, w, 0) gui.trace('Set horizontal pane: %s to position: %s', pane, pos) except TclError as e: # no sash to configure - last pane pass else: h = int(pane.winfo_height() * pos) try: pane.sash_place(0, 0, h) gui.trace('Set vertical pane: %s to position: %s', pane, pos) except TclError as e: # no sash to configure - last pane pass ##################################### # Label Frames ##################################### @contextmanager def labelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, **kwargs): name = kwargs.pop("label", kwargs.pop("name", None)) labelFg = kwargs.pop("labelFg", self.fg) try: lf = self.startLabelFrame(title, row, column, colspan, rowspan, sticky, hideTitle, name) except ItemLookupError: lf = self.openLabelFrame(title) self.configure(**kwargs) if not self.ttkFlag: lf.config(fg=labelFg) try: yield lf finally: self.stopLabelFrame() # sticky is alignment inside frame # frame will be added as other widgets def startLabelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, label=None, name=None): if label is not None: name = label if hideTitle: name = '' lf = self.startContainer(WIDGET_NAMES.LabelFrame, title, row, column, colspan, rowspan, sticky, name) return lf def stopLabelFrame(self): if self._getContainerProperty('type') != WIDGET_NAMES.LabelFrame: raise Exception("Can't stop a LABELFRAME, currently in:", self._getContainerProperty('type')) self.stopContainer() # function to set position of title for label frame def setLabelFrameTitle(self, title, newTitle): frame = self.widgetManager.get(WIDGET_NAMES.LabelFrame, title) frame.config(text=newTitle) ##################################### # Toggle Frames ##################################### @contextmanager def toggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs): try: tog = self.startToggleFrame(title, row, column, colspan, rowspan) except ItemLookupError: tog = self.openToggleFrame(title) self.configure(**kwargs) try: yield tog finally: self.stopToggleFrame() ######Â TOGGLE FRAMES ####### def startToggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0): return self.startContainer(WIDGET_NAMES.ToggleFrame, title, row, column, colspan, rowspan, sticky="new") def stopToggleFrame(self): if self._getContainerProperty('type') != WIDGET_NAMES.ToggleFrame: raise Exception("Can't stop a TOGGLEFRAME, currently in:", self._getContainerProperty('type')) self._getContainerProperty('container').stop() self.stopContainer() def toggleToggleFrame(self, title): toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title) toggle.toggle() def setToggleFrameText(self, title, newText): toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title) toggle.config(text=newText) def getToggleFrameState(self, title): toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title) return toggle.isShowing() ##################################### # Paged Windows ##################################### @contextmanager def pagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs): try: pw = self.startPagedWindow(title, row, column, colspan, rowspan) except ItemLookupError: pw = self.openPagedWindow(title) self.configure(**kwargs) try: yield pw finally: self.stopPagedWindow() ######Â PAGED WINDOWS ####### def startPagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0): return self.startContainer( WIDGET_NAMES.PagedWindow, title, row, column, colspan, rowspan, sticky="nsew") def setPagedWindowPage(self, title, page): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.showPage(page) def setPagedWindowButtonsTop(self, title, top=True): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.setNavPositionTop(top) def setPagedWindowButtons(self, title, buttons): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) if not isinstance(buttons, list) or len(buttons) != 2: raise Exception( "You must provide a list of two strings for setPagedWinowButtons()") pager.setPrevButton(buttons[0]) pager.setNextButton(buttons[1]) def setPagedWindowFunction(self, title, func): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) command = self.MAKE_FUNC(func, title) pager.registerPageChangeEvent(command) def getPagedWindowPageNumber(self, title): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) return pager.getPageNumber() def showPagedWindowPageNumber(self, title, show=True): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.showPageNumber(show) def showPagedWindowTitle(self, title, show=True): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.showTitle(show) def setPagedWindowTitle(self, title, pageTitle): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.setTitle(pageTitle) @contextmanager def page(self, windowTitle=None, pageNumber=None, sticky="nw", **kwargs): if windowTitle is None: pg = self.startPage(sticky) else: pg = self.openPage(windowTitle, pageNumber) self.configure(**kwargs) try: yield pg finally: self.stopPage() def startPage(self, sticky="nw"): if self._getContainerProperty('type') == WIDGET_NAMES.Page: self.warn("You didn't STOP the previous PAGE") self.stopPage() elif self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow: raise Exception("Can't start a PAGE, currently in:", self._getContainerProperty('type')) self.containerStack[-1]['widgets'] = True # generate a page title pageNum = self._getContainerProperty('container').frameStack.getNumFrames() + 1 pageTitle = self._getContainerProperty('title') + "__" + str(pageNum) return self.startContainer(WIDGET_NAMES.Page, pageTitle, row=None, column=None, colspan=None, rowspan=None, sticky=sticky) def stopPage(self): if self._getContainerProperty('type') == WIDGET_NAMES.Page: self.stopContainer() else: raise Exception("Can't stop PAGE, currently in:", self._getContainerProperty('type')) def stopPagedWindow(self): if self._getContainerProperty('type') == WIDGET_NAMES.Page: self.warn("You didn't STOP the previous PAGE") self.stopPage() if self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow: raise Exception("Can't stop a PAGEDWINDOW, currently in:", self._getContainerProperty('type')) self._getContainerProperty('container').stopPagedWindow() self.stopContainer() ##################################### # Scrolled Panes ##################################### @contextmanager def scrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): disabled = kwargs.pop("disabled", "") try: sp = self.startScrollPane(title, row, column, colspan, rowspan, sticky, disabled) except ItemLookupError: sp = self.openScrollPane(title) self.configure(**kwargs) try: yield sp finally: self.stopScrollPane() def startScrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", disabled=""): return self.startContainer(WIDGET_NAMES.ScrollPane, title, row, column, colspan, rowspan, sticky, disabled) # functions to stop the various containers def stopContainer(self): self._removeContainer() def stopScrollPane(self): if self._getContainerProperty('type') != WIDGET_NAMES.ScrollPane: raise Exception("Can't stop a SCROLLPANE, currently in:", self._getContainerProperty('type')) self.stopContainer() def stopAllPanedFrames(self): while True: try: self.stopPanedFrame() except: break ##################################### # Frames ##################################### @contextmanager def frame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): if title is None: # new subFrame fr = self.startFrame(title, row, column, colspan, rowspan, sticky) else: frameNumber = kwargs.pop('frameNumber', None) try: if frameNumber is not None: fr = self.openSubFrame(title, frameNumber) else: fr = self.openFrame(title) except: # no widget fr = self.startFrame(title, row, column, colspan, rowspan, sticky) self.configure(**kwargs) try: yield fr finally: self.stopFrame() def startFrame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): frameType = WIDGET_NAMES.Frame if self._getContainerProperty('type') == WIDGET_NAMES.FrameStack: # generate a frame title frameNum = self._getContainerProperty('container').getNumFrames() title = self._getContainerProperty('title') + "__" + str(frameNum) gui.trace("Adding new subFrame: %s", title) self.containerStack[-1]['widgets'] = True frameType = WIDGET_NAMES.SubFrame else: if title is None: raise Exception("All frames must have a title") gui.trace("Adding new frame: %s", title) return self.startContainer(frameType, title, row, column, colspan, rowspan, sticky) def stopFrame(self): if self._getContainerProperty('type') not in [WIDGET_NAMES.Frame, WIDGET_NAMES.SubFrame]: raise Exception("Can't stop a FRAME, currently in:", self._getContainerProperty('type')) self.stopContainer() def raiseFrame(self, title): ''' will bring the named frame in front of any others ''' gui.trace("Raising frame: %s", title) self.widgetManager.get(WIDGET_NAMES.Frame, title).lift() ##################################### # FrameStack ##################################### @contextmanager def frameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): change = kwargs.pop("change", None) start = kwargs.pop("start", -1) try: fr = self.startFrameStack(title, row, column, colspan, rowspan, sticky, change=change, start=start) except ItemLookupError: fr = self.openFrameStack(title) self.configure(**kwargs) try: yield fr finally: self.stopFrameStack() def startFrameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="news", change=None, start=-1): fs = self.startContainer(WIDGET_NAMES.FrameStack, title, row, column, colspan, rowspan, sticky) fs.setChangeFunction(change) fs.setStartFrame(start) return fs def stopFrameStack(self): if self._getContainerProperty('type') != WIDGET_NAMES.FrameStack: raise Exception("Can't stop a FRAMESTACK, currently in:", self._getContainerProperty('type')) self.stopContainer() def setStartFrame(self, title, num): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).setStartFrame(num) def nextFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showNextFrame(callFunction) def prevFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showPrevFrame(callFunction) def firstFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFirstFrame(callFunction) def lastFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showLastFrame(callFunction) def selectFrame(self, title, num, callFunction=True): if type(num) in (list, tuple): num = num[0] num = int(num) self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFrame(num, callFunction) def countFrames(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getNumFrames() def getCurrentFrame(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getCurrentFrame() def getPreviousFrame(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getPreviousFrame() def frameStackAtStart(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atStart() def frameStackAtEnd(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atEnd() ##################################### # SubWindows ##################################### @contextmanager def subWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True, **kwargs): visible = kwargs.pop("visible", None) try: sw = self.startSubWindow(name, title, modal, blocking, transient, grouped) except ItemLookupError: sw = self.openSubWindow(name) self.configure(**kwargs) try: yield sw finally: self.stopSubWindow() if visible is True: self.showSubWindow(name) def startSubWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True): self.widgetManager.verify(WIDGET_NAMES.SubWindow, name) gui.trace("Starting subWindow %s", name) top = SubWindow(self, self.topLevel, name, title=title, stopFunc = self.confirmHideSubWindow, modal=modal, blocking=blocking, transient=transient, grouped=grouped) ico = self._getTopLevel().winIcon self.widgetManager.add(WIDGET_NAMES.SubWindow, name, top) # now, add to top of stack self._addContainer(name, WIDGET_NAMES.SubWindow, top, 0, 1, "") # add an icon if required if ico is not None: self.setIcon(ico) else: top.winIcon = None return top def stopSubWindow(self): container = self.containerStack[-1] if container['type'] == WIDGET_NAMES.SubWindow: if not hasattr(container["container"], 'ms'): self.setMinSize(container["container"]) self.stopContainer() else: raise Exception("Can't stop a SUBWINDOW, currently in:", self._getContainerProperty('type')) def setSubWindowLocation(self, title, x, y): self.widgetManager.get(WIDGET_NAMES.SubWindow, title).setLocation(x, y) def showAllSubWindows(self): for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow): self.showSubWindow(sub) # functions to show/hide/destroy SubWindows def showSubWindow(self, title, hide=False, follow=False): tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) if hide: self.hideAllSubWindows() gui.trace("Showing subWindow %s", title) tl.show() self._bringToFront(tl) tl.block() return tl def hideAllSubWindows(self, useStopFunction=False): for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow): self.hideSubWindow(sub, useStopFunction) def hideSubWindow(self, title, useStopFunction=False): self.widgetManager.get(WIDGET_NAMES.SubWindow, title).hide(useStopFunction) def confirmHideSubWindow(self, title): self.hideSubWindow(title, True) def destroySubWindow(self, title): gui.trace("Destroying SubWindow %s", title) tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) tl.prepDestroy() # get rid of all the kids! self.cleanseWidgets(tl) def destroyAllSubWindows(self): gui.trace("Destroying all SubWindows") keys = list(self.widgetManager.group(WIDGET_NAMES.SubWindow).keys()) for k in keys: gui.trace("Destroying SubWindow: %s", k) wi = self.widgetManager.get(WIDGET_NAMES.SubWindow, k) self.cleanseWidgets(wi) # access has widgets stored in the standard widget store self.accessMade = False ##################################### # END containers ##################################### # function to destroy widget & all children # will also attempt to remove all trace from config dictionaries def cleanseWidgets(self, widget): widgType = gui.GET_WIDGET_CLASS(widget) gui.trace("Attempting to cleanse: %s", widgType) # make sure we've cleansed any children first for child in widget.winfo_children(): self.cleanseWidgets(child) if hasattr(widget, 'APPJAR_TYPE'): widgType = widget.APPJAR_TYPE widgName = WIDGET_NAMES.name(widgType) gui.trace("Cleansing: %s", widgName) if widgType not in [WIDGET_NAMES.Tab, WIDGET_NAMES.Page]: if not self.widgetManager.destroyWidget(widgType, widget): self.warn("Unable to destroy %s, during cleanse - destroy returned False", widgName) # must clear the frameLabel's label as well if widgType == WIDGET_NAMES.FrameLabel: gui.trace("Also Cleansing: %s", WIDGET_NAMES.name(WIDGET_NAMES.Label)) if not self.widgetManager.destroyWidget(WIDGET_NAMES.Label, widget): self.warn("Unable to destroy %s, during cleanse - destroy returned False", WIDGET_NAMES.Label) else: self.trace("Skipped %s, cleansed by parent", widgType) # need to remove if a container if widgName in WIDGET_NAMES.containers: self.trace("Destroying container: %s", widgName) self.widgetManager.destroyContainer(WIDGET_NAMES.ContainerLog, widget) # elif widgType in ('CanvasDnd', 'ValidationLabel', 'TabBorder', 'TabContainer', 'TabText', 'BgLabel') or hasattr(widget, 'SKIP_CLEANSE'): elif widgType in ('CanvasDnd', 'ValidationLabel', 'Grip', 'TabBorder', 'TabContainer', 'TabText', 'BgLabel') \ or widget.__dict__.get('SKIP_CLEANSE', False): pass # not logged in WidgetManager else: self.warn("Unable to destroy %s, during cleanse - NO APPJAR TYPE", gui.GET_WIDGET_CLASS(widget)) # functions to hide & show the main window def hide(self, btn=None): self._getTopLevel().displayed = False self._getTopLevel().withdraw() def show(self, btn=None): self._getTopLevel().displayed = True self._getTopLevel().deiconify() def setVisible(self, visible=True): if visible: self.show() else: self.hide() def getVisible(self): return self.topLevel.displayed visible = property(getVisible, setVisible) ##################################### # warn when bad functions called... ##################################### def __getattr__(self, name): def handlerFunction(*args, **kwargs): self.warn("Unknown function: <%s> Check your spelling, do you need more camelCase?", name) return handlerFunction def __setattr__(self, name, value): # would this create a new attribute? if self.built and not hasattr(self, name): raise AttributeError("Creating new attributes is not allowed!") super(gui, self).__setattr__(name, value) ##################################### # LabelBox Functions ##################################### # this will build a frame, with a label on the left hand side def _getLabelBox(self, title, **kwargs): self.widgetManager.verify(WIDGET_NAMES.Label, title) label = kwargs.pop('label', title) if label is True: label = title font = kwargs.pop('font', self._getContainerProperty('labelFont')) # first, make a frame frame = self._makeLabelBox()(self.getContainer()) if not self.ttkFlag: frame.config(background=self._getContainerBg()) self.widgetManager.log(WIDGET_NAMES.FrameBox, frame) # next make the label if self.ttkFlag: lab = ttk.Label(frame) else: lab = Label(frame, background=self._getContainerBg()) frame.theLabel = lab lab.hidden = False lab.inContainer = True lab.config( text=label, anchor=W, justify=LEFT, font=font ) if not self.ttkFlag: lab.config(background=self._getContainerBg()) lab.DEFAULT_TEXT = label self.widgetManager.add(WIDGET_NAMES.Label, title, lab) self.widgetManager.add(WIDGET_NAMES.FrameLabel, title, lab) # now put the label in the frame lab.pack(side=LEFT, fill=Y) return frame # this is where we add the widget to the frame built above def _packLabelBox(self, frame, widget): widget.pack(side=LEFT, fill=BOTH, expand=True) widget.inContainer = True frame.theWidget = widget #widget.grid( row=0, column=1, sticky=W+E ) #Grid.columnconfigure(frame, 1, weight=1) #Grid.rowconfigure(frame, 0, weight=1) # function to resize labels, if they are hidden or shown # not using this for two reasons: # - doesn't really work when font size changes # - breaks when things in containers # this can be made into a container property # def _updateLabelBoxes(self, title, column): # if len(title) >= self.labWidth.get(column, -1): # self.labWidth[column] = len(title) # # loop through other labels and resize # for na, wi in self.widgetManager.group(WIDGET_NAMES.FrameLabel).items(): # col = wi.master.grid_info().get("column", wi.master.master.grid_info().get("column", -1)) # if int(col) == column: # wi.config(width=self.labWidth[column]) ##################################### # FUNCTION for check boxes ##################################### def tick(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for checkBox() """ return self.checkBox(title, value, *args, **kwargs) def check(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for checkBox() """ return self.checkBox(title, value, *args, **kwargs) def checkBox(self, title, value=None, *args, **kwargs): """ adds, sets & gets checkBoxes all in one go """ widgKind = WIDGET_NAMES.CheckBox callFunction = kwargs.pop("callFunction", True) text = kwargs.pop("text", None) try: self.widgetManager.verify(widgKind, title) except: #widget exists if value is not None: self.setCheckBox(title, ticked=value, callFunction=callFunction) check = self.getCheckBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) check = self._checkBoxMaker(title, *args, **kwargs) if value is not None: self.setCheckBox(title, value) if text is not None: self.setCheckBoxText(title, text) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return check def _checkBoxMaker(self, title, value=None, kind="cb", row=None, column=0, colspan=0, rowspan=0, **kwargs): """ internal wrapper to hide kwargs from original add functions """ name = kwargs.pop("name", kwargs.pop('label', None)) return self.addCheckBox(title, row, column, colspan, rowspan, name) def addCheckBox(self, title, row=None, column=0, colspan=0, rowspan=0, name=None): ''' adds a new check box, at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.CheckBox, title) var = IntVar(self.topLevel) if name is None: name = title if not self.ttkFlag: cb = Checkbutton(self.getContainer(), text=name, variable=var) cb.config( font=self._getContainerProperty('labelFont'), background=self._getContainerBg(), activebackground=self._getContainerBg(), anchor=W) else: cb = ttk.Checkbutton(self.getContainer(), text=name, variable=var) cb.DEFAULT_TEXT = name cb.bind("<Button-1>", self._grabFocus) self.widgetManager.add(WIDGET_NAMES.CheckBox, title, cb) self.widgetManager.add(WIDGET_NAMES.CheckBox, title, var, group=WidgetManager.VARS) self._positionWidget(cb, row, column, colspan, rowspan, EW) return cb def setCheckBoxText(self, title, text): cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title) cb.DEFAULT_TEXT = text cb.config(text=text) def addNamedCheckBox(self, name, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a new check box, at the specified position, with the name as the text ''' return self.addCheckBox(title, row, column, colspan, rowspan, name) def getCheckBox(self, title): bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS) if bVar.get() == 1: return True else: return False def getAllCheckBoxes(self): cbs = {} for k in self.widgetManager.group(WIDGET_NAMES.CheckBox): cbs[k] = self.getCheckBox(k) return cbs def setCheckBox(self, title, ticked=True, callFunction=True): cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title) bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS) bVar.set(ticked) if ticked: if not self.ttkFlag: cb.select() else: cb.state(['selected']) else: if not self.ttkFlag: cb.deselect() else: cb.state(['!selected']) # now call function if callFunction: if hasattr(cb, 'cmd'): cb.cmd() def setCheckBoxBoxBg(self, title, newCol): self.setCheckBoxSelectColour(title, newCol) def setCheckBoxSelectColour(self, title, newCol): cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title) cb.config(selectcolor=newCol) def clearAllCheckBoxes(self, callFunction=False): for cb in self.widgetManager.group(WIDGET_NAMES.CheckBox): self.setCheckBox(cb, ticked=False, callFunction=callFunction) ##################################### # FUNCTION for scales ##################################### def slider(self, title, *args, **kwargs): """ simpleGUI - alternative for scale() """ return self.scale(title, *args, **kwargs) def scale(self, title, *args, **kwargs): """ simpleGUI - adds, sets & gets scales all in one go """ widgKind = WIDGET_NAMES.Scale vert = kwargs.pop("direction", "horizontal").lower() == "vertical" increment = kwargs.pop("increment", None) value = kwargs.pop("value", None) interval = kwargs.pop("interval", None) show = kwargs.pop("show", False) _range = kwargs.pop("range", None) callFunction = kwargs.pop("callFunction", True) label = kwargs.pop("label", False) try: self.widgetManager.verify(widgKind, title) except: # widget exists scale = self.getScale(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) scale = self._scaleMaker(title, label, *args, **kwargs) if _range is not None: self.setScaleRange(title, _range[0], _range[1]) if vert: self.setScaleVertical(title) if increment is not None: self.setScaleIncrement(title, increment) if interval is not None: self.showScaleIntervals(title, interval) if show: self.showScaleValue(title) if value is not None: self.setScale(title, value, callFunction) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return scale def _buildScale(self, title, frame): self.widgetManager.verify(WIDGET_NAMES.Scale, title) var = DoubleVar(self.topLevel) if not self.ttkFlag: scale = self._makeAjScale()(frame, increment=10, variable=var, repeatinterval=10, orient=HORIZONTAL, font=self._getContainerProperty('inputFont')) scale.config(digits=1, showvalue=False, highlightthickness=1) else: scale = self._makeAjScale()(frame, increment=10, variable=var, orient=HORIZONTAL) scale.bind("<Button-1>", self._grabFocus, "+") scale.var = var scale.inContainer = False self.widgetManager.add(WIDGET_NAMES.Scale, title, scale) return scale def _scaleMaker(self, title, label, row=None, column=0, colspan=0, rowspan=0, **kwargs): if label: return self.addLabelScale(title, row, column, colspan, rowspan, label) else: return self.addScale(title, row, column, colspan, rowspan) def addScale(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a slidable scale at the specified position ''' scale = self._buildScale(title, self.getContainer()) self._positionWidget(scale, row, column, colspan, rowspan) return scale def addLabelScale(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds a slidable scale, with a label showing the title at the specified position ''' frame = self._getLabelBox(title, label=label) scale = self._buildScale(title, frame) self._packLabelBox(frame, scale) self._positionWidget(frame, row, column, colspan, rowspan) return scale def getScale(self, title): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) return sc.get() def getAllScales(self): scales = {} for k in self.widgetManager.group(WIDGET_NAMES.Scale): scales[k] = self.getScale(k) return scales def setScale(self, title, pos, callFunction=True): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) with PauseCallFunction(callFunction, sc): sc.set(pos) def clearAllScales(self, callFunction=False): for sc in self.widgetManager.group(WIDGET_NAMES.Scale): self.setScale(sc, self.widgetManager.get(WIDGET_NAMES.Scale, sc).cget("from"), callFunction=callFunction) def setScaleIncrement(self, title, increment): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.increment = increment def setScaleLength(self, title, length): if not self.ttkFlag: sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(sliderlength=length) else: self.warn("ttk: setScaleLength() not supported: %s", title) # this will make the scale show interval numbers # set to 0 to remove def showScaleIntervals(self, title, intervals): if not self.ttkFlag: sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(tickinterval=intervals) else: self.warn("ttk: showScaleIntervals() not supported: %s", title) # this will make the scale show its value def showScaleValue(self, title, show=True): if not self.ttkFlag: sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(showvalue=show) else: self.warn("ttk: showScaleValue() not supported: %s", title) # change the orientation (Hor or Vert) def setScaleVertical(self, title): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(orient=VERTICAL) def setScaleHorizontal(self, title): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(orient=HORIZONTAL) def setScaleRange(self, title, start, end, curr=None): if curr is None: curr = start sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(from_=start, to=end) self.setScale(title, curr) # set the increment as 10% try: res = sc.cget("resolution") diff = int((((end - start)/res)/10)+0.99) # add 0.99 to round up... sc.increment = diff except: pass # resolution not supported in ttk ##################################### # FUNCTION for optionMenus ##################################### def combo(self, title, value=None, *args, **kwargs): """ shortner for optionBox() """ return self.optionBox(title, value, *args, **kwargs) def option(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for optionBox() """ return self.optionBox(title, value, *args, **kwargs) def optionbox(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for optionBox() """ return self.optionBox(title, value, *args, **kwargs) def optionBox(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets optionBoxes all in one go """ widgKind = WIDGET_NAMES.OptionBox kind = kwargs.pop("kind", "standard").lower().strip() label = kwargs.pop("label", False) callFunction = kwargs.pop("callFunction", True) override = kwargs.pop("override", False) checked = kwargs.pop("checked", True) selected = kwargs.pop("selected", None) disabled = kwargs.pop("disabled", "-") # select=set, replace=change, rename=rename, clear=clear, delete=delete if value is None: mode = 'get' else: mode = 'select' mode = kwargs.pop("mode", mode) index = kwargs.pop("index", None) newName = kwargs.pop("newName", None) try: self.widgetManager.verify(WIDGET_NAMES.OptionBox, title) except: # widget exists if mode == "select": if value is not None: self.setOptionBox(title, index=value, value=True, callFunction=callFunction, override=override) else: gui.error("No item specified to select in optionBox: %s", title) elif mode == "deselect": if value is not None: self.setOptionBox(title, index=value, value=False, callFunction=callFunction, override=override) else: self.clearOptionBox(title, callFunction=callFunction) gui.info("optionBox set back to its original state: %s", title) elif mode == "toggle": gui.error("Toggling optionboxes not supported: %s", title) elif mode == "clear": if value is not None: gui.error("No value should be specified wen clearing optionBox: %s", title) else: self.clearOptionBox(title, callFunction=callFunction) elif mode == "rename": if value is not None: self.renameOptionBox(title, item=value, newName=newName, callFunction=callFunction) else: gui.error("No item specified to rename in optionBox: %s", title) elif mode == "replace": if value is not None: self.changeOptionBox(title, options=value, index=index, callFunction=callFunction) else: gui.error("No values specified to replace in optionBox: %s", title) elif mode == "delete": if value is not None: self.deleteOptionBox(title, index=value) else: gui.error("No item specified to delete in optionBox: %s", title) elif mode == "get": pass else: gui.error("Invalid mode (%s) specified in optionBox: %s", mode, title) opt = self.getOptionBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "ticks": if label: opt = self.addLabelTickOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs) else: opt = self.addTickOptionBox(title, value, *args, disabled=disabled, **kwargs) else: if label: opt = self.addLabelOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs) else: opt = self.addOptionBox(title, value, *args, disabled=disabled, **kwargs) if selected is not None: self.setOptionBox(title, selected) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return opt def addDbOptionBox(self, title, db, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds an option box, with a list of tables form the specified database ''' data = self._getDbTables(db) opt = self.option(title, data, row, column, colspan, rowspan, **kwargs) opt.db = db return opt def _buildOptionBox(self, frame, title, options, kind="normal", disabled='-'): """ Internal wrapper, used for building OptionBoxes. It will use the kind to choose either a standard OptionBox or a TickOptionBox. ref: http://stackoverflow.com/questions/29019760/how-to-create-a-combobox-that-includes-checkbox-for-each-item :param frame: this should be a container, used as the parent for the OptionBox :param title: the key used to reference this OptionBox :param options: a list of values to put in the OptionBox, can be len 0 :param kind: the style of OptionBox: notmal or ticks :returns: the created OptionBox :raises ItemLookupError: if the title is already in use """ self.widgetManager.verify(WIDGET_NAMES.OptionBox, title) # create a string var to hold selected item var = StringVar(self.topLevel) self.widgetManager.add(WIDGET_NAMES.OptionBox, title, var, group=WidgetManager.VARS) maxSize, options = self._configOptionBoxList(title, options, kind) if len(options) > 0 and kind == "normal": option = ajOption(frame, var, *options) var.set(options[0]) option.kind = "normal" elif kind == "ticks": option = ajOption(frame, variable=var, value="") self._buildTickOptionBox(title, option, options) else: option = ajOption(frame, var, []) option.kind = "normal" option.config( justify=LEFT, font=self._getContainerProperty('inputFont'), # background=self._getContainerBg(), highlightthickness=0, width=maxSize, takefocus=1) option.bind("<Button-1>", self._grabFocus) # compare on windows & mac #option.config(highlightthickness=12, bd=0, highlightbackground=self._getContainerBg()) option.var = var option.maxSize = maxSize option.inContainer = False option.options = options option.disabled = disabled option.DEFAULT_TEXT="" if options is not None: option.DEFAULT_TEXT='\n'.join(str(x) for x in options) # if self.platform == self.MAC: # option.config(highlightbackground=self._getContainerBg()) option.bind("<Tab>", self._focusNextWindow) option.bind("<Shift-Tab>", self._focusLastWindow) # add a right click menu self._addRightClickMenu(option) # disable any separators self._disableOptionBoxSeparators(option) # add to array list self.widgetManager.add(WIDGET_NAMES.OptionBox, title, option) return option def _buildTickOptionBox(self, title, option, options): """ Internal wrapper, used for building TickOptionBoxes. Called by _buildOptionBox & changeOptionBox. Will add each of the options as a tick box, and use the title as a disabled header. :param title: the key used to reference this OptionBox :param option: an existing OptionBox that will be emptied & repopulated :param options: a list of values to put in the OptionBox, can be len 0 :returns: None - the option param is modified :raises ItemLookupError: if the title can't be found """ # delete any items - either the initial one when created, or any existing ones if changing option['menu'].delete(0, 'end') var = self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) var.set(title) vals = {} for o in options: vals[o] = BooleanVar() option['menu'].add_checkbutton( label=o, onvalue=True, offvalue=False, variable=vals[o]) self.widgetManager.update(WIDGET_NAMES.TickOptionBox, title, vals, group=WidgetManager.VARS) option.kind = "ticks" def addOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs): """ Adds a new standard OptionBox. Simply calls internal function _buildOptionBox. :param title: the key used to reference this OptionBox :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox :raises ItemLookupError: if the title is already in use """ option = self._buildOptionBox(self.getContainer(), title, options, disabled=disabled) self._positionWidget(option, row, column, colspan, rowspan) return option def addLabelOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs): """ Adds a new standard OptionBox, with a Label before it. Simply calls internal function _buildOptionBox, placing it in a LabelBox. :param title: the key used to reference this OptionBox and text for the Label :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use """ frame = self._getLabelBox(title, **kwargs) option = self._buildOptionBox(frame, title, options, disabled=disabled) self._packLabelBox(frame, option) self._positionWidget(frame, row, column, colspan, rowspan) return option def addTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs): """ Adds a new TickOptionBox. Simply calls internal function _buildOptionBox. :param title: the key used to reference this TickOptionBox :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox :raises ItemLookupError: if the title is already in use """ tick = self._buildOptionBox(self.getContainer(), title, options, kind="ticks", disabled=disabled) self._positionWidget(tick, row, column, colspan, rowspan) return tick def addLabelTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs): """ Adds a new TickOptionBox, with a Label before it Simply calls internal function _buildOptionBox, placing it in a LabelBox :param title: the key used to reference this TickOptionBox, and text for the Label :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use """ frame = self._getLabelBox(title, **kwargs) tick = self._buildOptionBox(frame, title, options, kind="ticks", disabled=disabled) self._packLabelBox(frame, tick) self._positionWidget(frame, row, column, colspan, rowspan) return tick def getOptionBox(self, title): """ Gets the selected item from the named OptionBox :param title: the OptionBox to check :returns: the selected item in an OptionBox or a dictionary of all items and their status for a TickOptionBox :raises ItemLookupError: if the title can't be found """ box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) if box.kind == "ticks": val = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) retVal = {} for k, v in val.items(): retVal[k] = bool(v.get()) return retVal else: val = self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) val = val.get().strip() # set to None if it's a divider if val.startswith("-") or len(val) == 0: val = None return val def getAllOptionBoxes(self): """ Convenience function to get the selected items for all OptionBoxes in the GUI. :returns: a dictionary containing the result of calling getOptionBox for every OptionBox/TickOptionBox in the GUI """ boxes = {} for k in self.widgetManager.group(WIDGET_NAMES.OptionBox): boxes[k] = self.getOptionBox(k) return boxes def _disableOptionBoxSeparators(self, box): """ Loops through all items in box and if they start with a dash, disables them :param box: the OptionBox to process :returns: None """ for pos, item in enumerate(box.options): if item.startswith(box.disabled): box["menu"].entryconfigure(pos, state="disabled") else: box["menu"].entryconfigure(pos, state="normal") def _configOptionBoxList(self, title, options, kind): """ Tidies up the list provided when an OptionBox is created/changed :param title: the title for the OptionBox - only used by TickOptionBox to calculate max size :param options: the list to tidy :param kind: The kind of option box (normal or ticks) :returns: a tuple containing the maxSize (width) and tidied list of items """ # deal with a dict_keys object - messy!!!! if not isinstance(options, list): options = list(options) # make sure all options are strings options = [str(i) for i in options] # check for empty strings, replace first with message, remove rest found = False newOptions = [] for pos, item in enumerate(options): if str(item).strip() == "": if not found: newOptions.append("- options -") found = True else: newOptions.append(item) options = newOptions # get the longest string length try: maxSize = len(str(max(options, key=len))) except: try: maxSize = len(str(max(options))) except: maxSize = 0 # increase if ticks if kind == "ticks": if len(title) > maxSize: maxSize = len(title) # new bug?!? - doesn't fit anymore! if self.platform == self.MAC: maxSize += 3 return maxSize, options def changeOptionBox(self, title, options, index=None, callFunction=False): """ Changes the entire contents of the named OptionBox ref: http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html :param title: the OptionBox to change :param options: the new values to put in the OptionBox :param index: an optional initial value to select :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ # get the optionBox & associated var box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) # tidy up list and get max size maxSize, options = self._configOptionBoxList(title, options, "normal") # warn if new options bigger if maxSize > box.maxSize: self.warn("The new options are wider then the old ones: %s > %s", maxSize, box.maxSize) if box.kind == "ticks": self._buildTickOptionBox(title, box, options) else: # delete the current options box['menu'].delete(0, 'end') # add the new items for option in options: box["menu"].add_command( label=option, command=lambda temp=option: box.setvar( box.cget("textvariable"), value=temp)) with PauseCallFunction(callFunction, box): box.var.set(options[0]) box.options = options # disable any separators self._disableOptionBoxSeparators(box) # select the specified option self.setOptionBox(title, index, callFunction=False, override=True) def deleteOptionBox(self, title, index): """ Deleted the specified item from the named OptionBox :param title: the OptionBox to change :param inde: the value to delete - either a numeric index, or the text of an item :returns: None :raises ItemLookupError: if the title can't be found """ self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) self.setOptionBox(title, index, value=None, override=True) def renameOptionBoxItem(self, title, item, newName=None, callFunction=False): """ Changes the text of the specified item in the named OptionBox :param title: the OptionBox to change :param item: the item to rename :param newName: the value to rename it with :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) self.setOptionBox(title, item, value=newName, callFunction=callFunction) def clearOptionBox(self, title, callFunction=True): """ Deselects any items selected in the named OptionBox If a TickOptionBox, all items will be set to False (unticked) :param title: the OptionBox to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) if box.kind == "ticks": # loop through each tick, set it to False ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) for k in ticks: self.setOptionBox(title, k, False, callFunction=callFunction) else: self.setOptionBox(title, 0, callFunction=callFunction, override=True) def clearAllOptionBoxes(self, callFunction=False): """ Convenience function to clear all OptionBoxes in the GUI Will simply call clearOptionBox on each OptionBox/TickOptionBox :param callFunction: whether to generate an event to notify that the widget has changed :returns: None """ for k in self.widgetManager.group(WIDGET_NAMES.OptionBox): self.clearOptionBox(k, callFunction) def setOptionBoxDisabledChar(self, title, disabled="-"): box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) box.disabled = disabled self._disableOptionBoxSeparators(box) def setOptionBox(self, title, index, value=True, callFunction=True, override=False): """ Main purpose is to select/deselect the item at the specified position But will also: delete an item if value is set to None or rename an item if value is set to a String :param title: the OptionBox to change :param index: the position or value of the item to select/delete :param value: determines what to do to the item: if set to None, will delete the item, else it sets the items state :param callFunction: whether to generate an event to notify that the widget has changed :param override: if set to True, allows a disabled item to be selected :returns: None :raises ItemLookupError: if the title can't be found """ box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) if box.kind == "ticks": gui.trace("Updating tickOptionBox") ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) if index is None: gui.trace("Index empty - nothing to update") return elif index in ticks: gui.trace("Updating: %s", index) tick = ticks[index] try: index_num = box.options.index(index) except: self.warn("Unknown tick: %s in OptionBox: %s", index, title) return with PauseCallFunction(callFunction, tick, useVar=False): if value is None: # then we need to delete it gui.trace("Deleting tick: %s from OptionBox %s", index, title) box['menu'].delete(index_num) del(box.options[index_num]) self.widgetManager.remove(WIDGET_NAMES.TickOptionBox, title, index, group=WidgetManager.VARS) elif isinstance(value, bool): gui.trace("Updating tick: %s from OptionBox: %s to: %s", index, title, value) tick.set(value) else: gui.trace("Renaming tick: %s from OptionBox: %s to: %s", index, title, value) ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) ticks[value] = ticks.pop(index) box.options[index_num] = value self.changeOptionBox(title, box.options) for tick in ticks: self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)[tick].set(ticks[tick].get()) else: if value is None: self.warn("Unknown tick in deleteOptionBox: %s in OptionBox: %s" , index, title) else: self.warn("Unknown tick in setOptionBox: %s in OptionBox: %s", index, title) else: gui.trace("Updating regular optionBox: %s at: %s to: %s", title, index, value) count = len(box.options) if count > 0: if index is None: index = 0 if not isinstance(index, int): try: index = box.options.index(index) except: if value is None: self.warn("Unknown option in deleteOptionBox: %s in OptionBox: %s", index, title) else: self.warn("Unknown option in setOptionBox: %s in OptionBox: %s", index, title) return gui.trace("--> index now: %s", index) if index < 0 or index > count - 1: self.warn("Invalid option: %s. Should be between 0 and %s." , count-1, index) else: if value is None: # then we can delete it... gui.trace("Deleting option: %s from OptionBox: %s", index, title) box['menu'].delete(index) del(box.options[index]) self.setOptionBox(title, 0, callFunction=False, override=override) elif isinstance(value, bool): gui.trace("Updating: OptionBox: %s to: %s", title, index) with PauseCallFunction(callFunction, box): if not box['menu'].invoke(index): if override: gui.trace("Setting OptionBox: %s to disabled option: %s", title, index) box["menu"].entryconfigure(index, state="normal") box['menu'].invoke(index) box["menu"].entryconfigure(index, state="disabled") else: self.warn("Unable to set disabled option: %s in OptionBox %s. Try setting 'override=True'", index, title) else: gui.trace("Invoked item: %s", index) else: gui.trace("Renaming: %s from OptionBox: %s to: %s", index, title, value) pos = box.options.index(self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).get()) box.options[index] = value self.changeOptionBox(title, box.options, pos) else: self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).set("") self.warn("No items to select from: %s", title) ##################################### # FUNCTION for GoogleMaps ##################################### def map(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets maps all in one go """ widgKind = WIDGET_NAMES.Map zoom = kwargs.pop("zoom", None) size = kwargs.pop("size", None) terrain = kwargs.pop("terrain", None) proxy = kwargs.pop("proxy", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists gMap = self.getLabel(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) gMap = self.addGoogleMap(title, *args, **kwargs) if value is not None: self.setGoogleMapLocation(title, value) if zoom is not None: self.setGoogleMapZoom(title, zoom) if size is not None: self.setGoogleMapSize(title, size) if terrain is not None: self.setGoogleMapTerrain(title, terrain) if proxy is not None: self.setGoogleMapProxy(title, proxy) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return gMap def addGoogleMap(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a GoogleMap widget at the specified position ''' self._loadURL() self._loadTooltip() if urlencode is False: raise Exception("Unable to load GoogleMaps - urlencode library not available") self.widgetManager.verify(WIDGET_NAMES.Map, title) gMap = GoogleMap(self.getContainer(), self, useTtk = self.ttkFlag, font=self._getContainerProperty('labelFont')) self._positionWidget(gMap, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Map, title, gMap) return gMap def setGoogleMapProxy(self, title, proxyString): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) gMap.setProxyString(proxyString) def setGoogleMapLocation(self, title, location): self.searchGoogleMap(title, location) def searchGoogleMap(self, title, location): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) gMap.changeLocation(location) def setGoogleMapTerrain(self, title, terrain): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if terrain not in gMap.TERRAINS: raise Exception("Invalid terrain. Must be one of " + str(gMap.TERRAINS)) gMap.changeTerrain(terrain) def setGoogleMapZoom(self, title, mod): self. zoomGoogleMap(title, mod) def zoomGoogleMap(self, title, mod): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if mod in ["+", "-"]: gMap.zoom(mod) elif isinstance(mod, int) and 0 <= mod <= 22: gMap.setZoom(mod) def setGoogleMapSize(self, title, size): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) gMap.setSize(size) def setGoogleMapMarker(self, title, location, size=None, colour=None, label=None, replace=False): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if len(location) == 0: gMap.removeMarkers() else: gMap.addMarker(location, size, colour, label, replace) def removeGoogleMapMarker(self, title, label): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if len(label) == 0: gMap.removeMarkers() else: gMap.removeMarker(label) def getGoogleMapZoom(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["zoom"] def getGoogleMapTerrain(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["maptype"].title() def getGoogleMapLocation(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["center"] def getGoogleMapSize(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["size"] def saveGoogleMap(self, title, fileLocation): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) return gMap.saveTile(fileLocation) ##################################### # FUNCTION for matplotlib ##################################### def plot(self, title, t=None, s=None, *args, **kwargs): """ simpleGUI - adds, sets & gets plots all in one go """ widgKind = WIDGET_NAMES.Plot nav = kwargs.pop("nav", kwargs.pop("showNav", False)) try: self.widgetManager.verify(widgKind, title) except: # widget exists keepLabels = kwargs.pop("keepLabels", False) self.updatePlot(title, t, s, keepLabels=keepLabels) plot = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if t is not None: if s is not None: plot = self.addPlot(title, t, s, *args, showNav=nav, **kwargs) else: gui.warn("Invalid parameters for plot: must provide t & s") return None else: plot = self.addPlotFig(title, *args, showNav=nav, **kwargs) return plot def addPlot(self, title, t, s, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False): ''' adds a MatPlotLib, with t/s plotted ''' canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav) axes = fig.add_subplot(111) axes.plot(t,s) canvas.axes = axes return axes def addPlotFig(self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False): canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav) return fig def _addPlotFig(self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False): self.widgetManager.verify(WIDGET_NAMES.Plot, title) self._loadMatplotlib() if PlotCanvas is False: raise Exception("Unable to load MatPlotLib - plots not available") else: fig = PlotFig(tight_layout=True) if width is not None and height is not None: fig.set_size_inches(width,height,forward=True) frame = frameBase(self.getContainer()) canvas = PlotCanvas(fig, frame) canvas._tkcanvas.config(background="#c0c0c0", borderwidth=0, highlightthickness=0) canvas.fig = fig canvas.draw() if showNav: navBar = PlotNav(canvas, frame) navBar.pack(side=TOP, fill=X, expand=0) canvas._tkcanvas.pack(side=TOP, fill=BOTH, expand=1) # self._positionWidget(canvas.get_tk_widget(), row, column, colspan, rowspan) self._positionWidget(frame, row, column, colspan, rowspan, sticky='news') self.widgetManager.add(WIDGET_NAMES.Plot, title, canvas) return canvas, fig def refreshPlot(self, title): canvas = self.widgetManager.get(WIDGET_NAMES.Plot, title) canvas.draw() def updatePlot(self, title, t, s, keepLabels=False): axes = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes if keepLabels: xLab = axes.get_xlabel() yLab = axes.get_ylabel() pTitle = axes.get_title() handles, legends = axes.get_legend_handles_labels() axes.clear() axes.plot(t, s) if keepLabels: axes.set_xlabel(xLab) axes.set_ylabel(yLab) axes.set_title(pTitle) axes.legend(handles, legends) self.refreshPlot(title) return axes ##################################### # FUNCTION to manage Properties Widgets ##################################### def properties(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets properties all in one go """ widgKind = WIDGET_NAMES.Properties try: self.widgetManager.verify(widgKind, title) except: # widget exists # if value is not None: # need to work out args... # self.setProperty(title, prop=value) props = self.getProperties(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) props = self.addProperties(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return props def addProperties(self, title, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a new properties widget, displaying the dictionary of booleans as tick boxes ''' self.widgetManager.verify(WIDGET_NAMES.Properties, title) haveTitle = True if self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame: self.containerStack[-1]['sticky'] = "ew" haveTitle = False props = Properties(self.getContainer(), title, values, haveTitle, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) self._positionWidget(props, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Properties, title, props) return props def getProperties(self, title): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) return props.getProperties() def getAllProperties(self): props = {} for k in self.widgetManager.group(WIDGET_NAMES.Properties): props[k] = self.getProperties(k) return props def getProperty(self, title, prop): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) return props.getProperty(prop) def setProperty(self, title, prop, value=False, callFunction=True): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.addProperty(prop, value, callFunction=callFunction) def setProperties(self, title, props, callFunction=True): p = self.widgetManager.get(WIDGET_NAMES.Properties, title) p.addProperties(props, callFunction=callFunction) def deleteProperty(self, title, prop): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.addProperty(prop, None, callFunction=False) def setPropertyText(self, title, prop, newText=None): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.renameProperty(prop, newText) def setPropertiesBoxBg(self, title, newCol): self.setPropertiesSelectColour(title, newCol) def setPropertiesSelectColour(self, title, newCol): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.config(selectcolor=newCol) def clearProperties(self, title, callFunction=True): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.clearProperties(callFunction) def clearAllProperties(self, callFunction=False): props = {} for k in self.widgetManager.group(WIDGET_NAMES.Properties): self.clearProperties(k, callFunction) def resetProperties(self, title, callFunction=True): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.resetProperties(callFunction) def resetAllProperties(self, callFunction=False): props = {} for k in self.widgetManager.group(WIDGET_NAMES.Properties): self.resetProperties(k, callFunction) ##################################### # FUNCTION to add spin boxes ##################################### def spin(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for spinBox() """ return self.spinBox(title, value, *args, **kwargs) def spinbox(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for spinBox() """ return self.spinBox(title, value, *args, **kwargs) def spinBox(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets spinBoxes all in one go """ widgKind = WIDGET_NAMES.SpinBox endValue = kwargs.pop("endValue", None) selected = kwargs.pop("selected", None) item = kwargs.pop("item", None) label = kwargs.pop("label", False) # select=select, deselect=<RESET>, toggle=<NONE>, clear=??, rename=set, replace=update, delete=remov if value is None: mode = 'get' else: mode = 'select' mode = kwargs.pop("mode", mode) callFunction = kwargs.pop("callFunction", True) try: self.widgetManager.verify(widgKind, title) except: # widget exists if mode == "select": if value is not None: self.setSpinBoxPos(title, value, *args, **kwargs) else: gui.error("No item specified to select in spinbox: %s", title) elif mode == "toggle": gui.error("%s not available on spinbox: %s", mode, title) elif mode in["clear", "deselect"]: self.clearSpinBox(title) elif mode == "rename": gui.error("%s not implemented yet in spinbox: %s", mode, title) elif mode == "replace": if value is not None: self.changeSpinBox(title, vals=value) else: gui.error("No values specified to replace in spinbox: %s", title) elif mode == "delete": gui.error("%s not implemented yet in spinbox: %s", mode, title) elif mode == "get": pass else: gui.error("Invalid mode (%s) specified in spinbox: %s", mode, title) spinBox = self.getSpinBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if endValue is not None: if label: spinBox = self.addLabelSpinBoxRange(title, value, endValue, *args, label=label, **kwargs) else: spinBox = self.addSpinBoxRange(title, value, endValue, *args, **kwargs) else: if label: spinBox = self.addLabelSpinBox(title, value, *args, label=label, **kwargs) else: spinBox = self.addSpinBox(title, value, *args, **kwargs) if selected is not None: self.setSpinBoxPos(title, selected) if item is not None: self.setSpinBox(title, item) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return spinBox def _buildSpinBox(self, frame, title, vals): self.widgetManager.verify(WIDGET_NAMES.SpinBox, title) if type(vals) not in [list, tuple]: raise Exception("Can't create SpinBox " + title + ". Invalid values: " + str(vals)) spin = Spinbox(frame) spin.var = StringVar(self.topLevel) spin.config(textvariable=spin.var) spin.inContainer = False spin.isRange = False spin.config(font=self._getContainerProperty('inputFont'), highlightthickness=0) # adds bg colour under spinners # if self.platform == self.MAC: # spin.config(highlightbackground=self._getContainerBg()) spin.bind("<Tab>", self._focusNextWindow) spin.bind("<Shift-Tab>", self._focusLastWindow) # store the vals in DEFAULT_TEXT spin.DEFAULT_TEXT="" if vals is not None: spin.DEFAULT_TEXT='\n'.join(str(x) for x in vals) self._populateSpinBox(spin, vals) # prevent invalid entries if self.validateSpinBox is None: self.validateSpinBox = ( self.containerStack[0]['container'].register( self._validateSpinBox), '%P', '%W') spin.config(validate='all', validatecommand=self.validateSpinBox) self.widgetManager.add(WIDGET_NAMES.SpinBox, title, spin) return spin def _populateSpinBox(self, spin, vals, reverse=True): # make sure it's a list #Â reverse it, so the spin box functions properly if reverse: vals = list(vals) vals.reverse() vals = tuple(vals) spin.config(values=vals) def _addSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0): spin = self._buildSpinBox(self.getContainer(), title, values) self._positionWidget(spin, row, column, colspan, rowspan) self.setSpinBoxPos(title, 0) return spin def addSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a spinbox, with the specified values ''' return self._addSpinBox(title, values, row, column, colspan, rowspan) def addLabelSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a spinbox, with the specified values, and a label displaying the title ''' frame = self._getLabelBox(title, **kwargs) spin = self._buildSpinBox(frame, title, values) self._packLabelBox(frame, spin) self._positionWidget(frame, row, column, colspan, rowspan) self.setSpinBoxPos(title, 0) return spin def addSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a spinbox, with a range of whole numbers ''' vals = list(range(fromVal, toVal + 1)) spin = self._addSpinBox(title, vals, row, column, colspan, rowspan) spin.isRange = True return spin def addLabelSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, label=True, **kwargs): ''' adds a spinbox, with a range of whole numbers, and a label displaying the title ''' vals = list(range(fromVal, toVal + 1)) spin = self.addLabelSpinBox(title, vals, row, column, colspan, rowspan, label=label) spin.isRange = True return spin def getSpinBox(self, title): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) return spin.get() def getAllSpinBoxes(self): boxes = {} for k in self.widgetManager.group(WIDGET_NAMES.SpinBox): boxes[k] = self.getSpinBox(k) return boxes # validates that an item in the named spinbox starts with the user_input def _validateSpinBox(self, user_input, widget_name): spin = self.containerStack[0]['container'].nametowidget(widget_name) vals = spin.cget("values") # .split() vals = self._getSpinBoxValsAsList(vals) for i in vals: if i.startswith(user_input): return True self.containerStack[0]['container'].bell() return False # expects a valid spin box widget, and a valid value def _setSpinBoxVal(self, spin, val, callFunction=True): # now call function with PauseCallFunction(callFunction, spin): spin.var.set(val) # is it going to be a hash or list?? def _getSpinBoxValsAsList(self, vals): vals.replace("{", "") vals.replace("}", "") # if "{" in vals: # vals = vals[1:-1] # vals = vals.split("} {") # else: vals = vals.split() return vals def setSpinBox(self, title, value, callFunction=True): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) vals = spin.cget("values") # .split() vals = self._getSpinBoxValsAsList(vals) val = str(value) if val not in vals: raise Exception( "Invalid value: " + val + ". Not in SpinBox: " + title + "=" + str(vals)) self._setSpinBoxVal(spin, val, callFunction) def clearSpinBox(self, title, callFunction=False): self.setSpinBoxPos(title, 0, callFunction=callFunction) def clearAllSpinBoxes(self, callFunction=False): for sb in self.widgetManager.group(WIDGET_NAMES.SpinBox): self.setSpinBoxPos(sb, 0, callFunction=callFunction) def setSpinBoxPos(self, title, pos, callFunction=True): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) vals = spin.cget("values") # .split() vals = self._getSpinBoxValsAsList(vals) pos = int(pos) if pos < 0 or pos >= len(vals): raise Exception( "Invalid position: " + str(pos) + ". No position in SpinBox: " + title + "=" + str(vals)) pos = len(vals) - 1 - pos val = vals[pos] self._setSpinBoxVal(spin, val, callFunction) def changeSpinBox(self, title, vals, reverse=True): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) if spin.isRange: self.warn("Can't convert %s RangeSpinBox to SpinBox", title) else: self._populateSpinBox(spin, vals, reverse) self.setSpinBoxPos(title, 0) ##################################### # FUNCTION to add images ##################################### def image(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets images all in one go """ widgKind = WIDGET_NAMES.Image kind = kwargs.pop("kind", "standard").lower().strip() speed = kwargs.pop("speed", None) drop = kwargs.pop("drop", None) over = kwargs.pop("over", None) submit = kwargs.pop("submit", None) _map = kwargs.pop("map", None) try: self.widgetManager.verify(widgKind, title) except: # already exists if value is not None: if kind == "data": self.setImageData(title, value, **kwargs) elif kind == "icon": gui.warn("Changing image icons not yet supported: %s.", title) else: self.setImage(title, value) image = self.getImage(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "icon": image = self.addIcon(title, value, *args, **kwargs) elif kind == "data": image = self.addImageData(title, value, *args, **kwargs) else: image = self.addImage(title, value, *args, **kwargs) if speed is not None: self.setAnimationSpeed(title, speed) if over is not None: self.setImageMouseOver(title, over) if submit is not None: if _map is not None: self.setImageMap(title, submit, _map) else: self.setImageSubmitFunction(title, submit) elif submit is None and _map is not None: gui.warn("Must specify a submit function when setting an image map: %s", title) if drop is not None: self.setImageDropTarget(title, drop) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return image # looks up label containing image def _animateImage(self, title, firstTime=False): if not self.alive: return try: lab = self.widgetManager.get(WIDGET_NAMES.Image, title) except ItemLookupError: # image destroyed... try: self.widgetManager.remove(WIDGET_NAMES.AnimationID, title) except: pass return if not lab.image.animating: self.widgetManager.remove(WIDGET_NAMES.AnimationID, title) return if firstTime and lab.image.alreadyAnimated: return lab.image.alreadyAnimated = True try: if lab.image.cached: pic = lab.image.pics[lab.image.anim_pos] else: pic = PhotoImage(file=lab.image.path, format="gif - {0}".format(lab.image.anim_pos)) lab.image.pics.append(pic) lab.image.anim_pos += 1 lab.config(image=pic) anim_id = self.topLevel.after(int(lab.image.anim_speed), self._animateImage, title) self.widgetManager.update(WIDGET_NAMES.AnimationID, title, anim_id) except IndexError: # will be thrown when we reach end of anim images lab.image.anim_pos = 0 lab.image.cached = True self._animateImage(title) except TclError: # will be thrown when all images cached lab.image.anim_pos = 0 lab.image.cached = True self._animateImage(title) def _preloadAnimatedImage(self, img): if not self.alive: return if img.cached: return try: pic = PhotoImage(file=img.path, format="gif - {0}".format(img.anim_pos)) img.pics.append(pic) img.anim_pos += 1 self.preloadAnimatedImageId = self.topLevel.after( 0, self._preloadAnimatedImage, img) # when all frames have been processed except TclError as e: # expected - when all images cached img.anim_pos = 0 img.cached = True def _configAnimatedImage(self, img): img.alreadyAnimated = False img.isAnimated = True img.pics = [] img.cached = False img.anim_pos = 0 img.anim_speed = 150 img.animating = True # simple way to check if image is animated def _checkIsAnimated(self, name): if imghdr.what(name) == "gif": try: PhotoImage(file=name, format="gif - 1") return True except: pass return False def setAnimationSpeed(self, name, speed): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image if speed < 1: speed = 1 self.warn("Setting %s speed to 1. Minimum animation speed is 1.", name) img.anim_speed = int(speed) def stopAnimation(self, name): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image img.animating = False def startAnimation(self, name): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image if not img.animating: img.animating = True anim_id = self.topLevel.after(img.anim_speed, self._animateImage, name) self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id) # function to set an alternative image, when a mouse goes over def setImageMouseOver(self, title, overImg): lab = self.widgetManager.get(WIDGET_NAMES.Image, title) # first check over image & cache it fullPath = self.getImagePath(overImg) self.topLevel.after(0, self._getImage, fullPath) leaveImg = lab.image.path lab.bind("<Leave>", lambda e: self.setImage(title, leaveImg, True)) lab.bind("<Enter>", lambda e: self.setImage(title, fullPath, True)) lab.hasMouseOver = True # function to set an image location def setImageLocation(self, location): if os.path.isdir(location): self.userImages = location else: raise Exception("Invalid image location: " + location) # get the full path of an image (including image folder) def getImagePath(self, imagePath): if imagePath is None: return None if self.userImages is not None: imagePath = os.path.join(self.userImages, imagePath) absPath = os.path.abspath(imagePath) return absPath # function to see if an image has changed def hasImageChanged(self, originalImage, newImage): newAbsImage = self.getImagePath(newImage) if originalImage is None: return True # filename has changed if originalImage.path != newAbsImage: return True # modification time has changed if originalImage.modTime != os.path.getmtime(newAbsImage): return True # no changes return False # function to remove image objects form cache def clearImageCache(self): self.widgetManager.clear(WIDGET_NAMES.ImageCache) # internal function to build an image function from a string def _getImageData(self, imageData, fmt="gif"): if fmt=="png": self._importPngimagetk() if PngImageTk is False: raise Exception("TKINTERPNG library not found, PNG files not supported: imageData") if sys.version_info >= (2, 7): self.warn("Image processing for .PNGs is slow. .GIF is the recommended format") # png = PngImageTk(imagePath) # png.convert() # photo = png.image else: raise Exception("PNG images only supported in python 3: imageData") elif fmt == "gif": imgObj = PhotoImage(data=imageData) else: # expect we already have a PhotoImage object, for example created by PIL imgObj = imageData imgObj.path = None imgObj.modTime = datetime.datetime.now() imgObj.isAnimated = False imgObj.animating = False return imgObj # internal function to check/build image object def _getImage(self, imagePath, checkCache=True, addToCache=True): if imagePath is None: return None # get the full image path imagePath = self.getImagePath(imagePath) # if we're caching, and we have a non-None entry in the cache - get it... photo = None if checkCache and imagePath in self.widgetManager.group(WIDGET_NAMES.ImageCache) and self.widgetManager.get(WIDGET_NAMES.ImageCache, imagePath) is not None: photo = self.widgetManager.get(WIDGET_NAMES.ImageCache, imagePath) # if the image hasn't changed, use the cache if not self.hasImageChanged(photo, imagePath): pass # else load a new one elif os.path.isfile(imagePath): if os.access(imagePath, os.R_OK): imgType = imghdr.what(imagePath) if imgType is None: raise Exception( "Invalid file: " + imagePath + " is not a valid image") elif not imagePath.lower().endswith(imgType) and not ( imgType == "jpeg" and imagePath.lower().endswith("jpg")): # the image has been saved with the wrong extension raise Exception( "Invalid image extension: " + imagePath + " should be a ." + imgType) elif imagePath.lower().endswith('.gif'): photo = PhotoImage(file=imagePath) elif imagePath.lower().endswith('.ppm') or imagePath.lower().endswith('.pgm'): photo = PhotoImage(file=imagePath) elif imagePath.lower().endswith('jpg') or imagePath.lower().endswith('jpeg'): self.warn("Image processing for .JPGs is slow. .GIF is the recommended format") photo = self.convertJpgToBmp(imagePath) elif imagePath.lower().endswith('.png'): # known issue here, some PNGs lack IDAT chunks # also, PNGs seem broken on python<3, maybe around the map # function used to generate pixel maps self._importPngimagetk() if PngImageTk is False: raise Exception( "TKINTERPNG library not found, PNG files not supported: " + imagePath) if sys.version_info >= (2, 7): self.warn("Image processing for .PNGs is slow. .GIF is the recommended format") png = PngImageTk(imagePath) png.convert() photo = png.image else: raise Exception("PNG images only supported in python 3: " + imagePath) else: raise Exception("Invalid image type: " + imagePath) else: raise Exception("Can't read image: " + imagePath) else: raise Exception("Image " + imagePath + " does not exist") # store the full path to this image photo.path = imagePath # store the modification time photo.modTime = os.path.getmtime(imagePath) # sort out if it's an animated image if self._checkIsAnimated(imagePath): self._configAnimatedImage(photo) self._preloadAnimatedImage(photo) else: photo.isAnimated = False photo.animating = False if addToCache: self.widgetManager.update(WIDGET_NAMES.ImageCache, imagePath, photo) return photo def getImageDimensions(self, name): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image return img.width(), img.height() # force replace the current image, with a new one def reloadImage(self, name, imageFile): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = self._getImage(imageFile, False) self._populateImage(name, image) def reloadImageData(self, name, imageData, fmt="gif"): self.setImageData(name, imageData, fmt) def setImageData(self, name, imageData, fmt="gif"): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = self._getImageData(imageData, fmt=fmt) self._populateImage(name, image) # replace the current image, with a new one def getImage(self, name): label = self.widgetManager.get(WIDGET_NAMES.Image, name) return label.image.path def setImage(self, name, imageFile, internal=False): label = self.widgetManager.get(WIDGET_NAMES.Image, name) imageFile = self.getImagePath(imageFile) # only set the image if it's different if label.image is not None and label.image.path == imageFile: self.warn("Not updating %s, %s hasn't changed." , name, imageFile) return elif imageFile is None: return else: image = self._getImage(imageFile) self._populateImage(name, image, internal) # internal function to update the image in a label def _populateImage(self, name, image, internal=False): label = self.widgetManager.get(WIDGET_NAMES.Image, name) if label.image is not None: label.image.animating = False label.config(image=image) label.config(anchor=CENTER, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: label.config(background=self._getContainerBg()) label.image = image # keep a reference! if image.isAnimated: anim_id = self.topLevel.after( image.anim_speed + 100, self._animateImage, name, True) self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id) if not internal and label.hasMouseOver: leaveImg = label.image.path label.bind("<Leave>", lambda e: self.setImage(name, leaveImg, True)) # removed - keep the label the same size, and crop images #h = image.height() #w = image.width() #label.config(height=h, width=w) self.topLevel.update_idletasks() # function to configure an image map def setImageMap(self, name, func, coords): self._setWidgetMap(name, WIDGET_NAMES.Image, func, coords) def _setWidgetMap(self, name, _type, func, coords): widget = self.widgetManager.get(_type, name) rectangles = [] if len(coords) > 0: for k, v in coords.items(): rect = AjRectangle(k, AjPoint(v[0], v[1]), v[2]-v[0], v[3]-v[1]) rectangles.append(rect) widget.MAP_COORDS = rectangles widget.MAP_FUNC = func widget.bind("<Button-1>", lambda e: self._widgetMap(_type, name, e), add="+") # function called when an image map is clicked def _widgetMap(self, _type, name, event): widget = self.widgetManager.get(_type, name) for rect in widget.MAP_COORDS: if rect.contains(AjPoint(event.x, event.y)): widget.MAP_FUNC(rect.name) return widget.MAP_FUNC("UNKNOWN: " + str(event.x) + ", " + str(event.y)) def addImage(self, name, imageFile, row=None, column=0, colspan=0, rowspan=0, compound=None): ''' Adds an image at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.Image, name) imgObj = self._getImage(imageFile) self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound) self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False return imgObj def addIcon(self, name, iconName, row=None, column=0, colspan=0, rowspan=0, compound=None): ''' adds one of the built-in icons at the specified position ''' icon = os.path.join(self.icon_path, iconName.lower()+".png") with PauseLogger(): return self.addImage(name, icon, row, column, colspan, rowspan, compound=compound) def addImageData(self, name, imageData, row=None, column=0, colspan=0, rowspan=0, fmt="gif", compound=None): ''' load image from base-64 encoded GIF use base64 module to convert binary data to base64 ''' self.widgetManager.verify(WIDGET_NAMES.Image, name) imgObj = self._getImageData(imageData, fmt) self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound) self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False return imgObj def _addImageObj(self, name, img, row=None, column=0, colspan=0, rowspan=0, compound=None): if not self.ttkFlag: label = Label(self.getContainer()) label.config(background=self._getContainerBg()) else: label = ttk.Label(self.getContainer()) label.config(anchor=CENTER, font=self._getContainerProperty('labelFont'),image=img) label.image = img # keep a reference! if compound is not None: label.config(text=name, compound=compound) if img is not None and compound is None and not self.ttkFlag: h = img.height() w = img.width() label.config(height=h, width=w) self.widgetManager.add(WIDGET_NAMES.Image, name, label) self._positionWidget(label, row, column, colspan, rowspan) if img is not None and img.isAnimated: anim_id = self.topLevel.after( img.anim_speed, self._animateImage, name, True) self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id) def setImageSize(self, name, width, height): img = self.widgetManager.get(WIDGET_NAMES.Image, name) img.config(height=height, width=width) # def rotateImage(self, name, image): # img = self.widgetManager.get(WIDGET_NAMES.Image, name) # if +ve then grow, else shrink... def zoomImage(self, name, x, y=''): if x <= 0: self.shrinkImage(name, x * -1, y * -1) else: self.growImage(name, x, y) # get every nth pixel (must be an integer) # 0 will return an empty image, 1 will return the image, 2 will be 1/2 the # size ... def shrinkImage(self, name, x, y=''): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = label.image.subsample(x, y) label.config(image=image) label.config(anchor=CENTER, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: label.config(background=self._getContainerBg()) label.config(width=image.width(), height=image.height()) label.modImage = image # keep a reference! # get every nth pixel (must be an integer) # 0 won't work, 1 will return the original size def growImage(self, name, x, y=''): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = label.image.zoom(x, y) label.config(image=image) label.config(anchor=CENTER, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: label.config(background=self._getContainerBg()) label.config(width=image.width(), height=image.height()) label.modImage = image # keep a reference! def convertJpgToBmp(self, image): self._loadNanojpeg() if nanojpeg is False: raise Exception( "nanojpeg library not found, unable to display jpeg files: " + image) elif sys.version_info < (2, 7): raise Exception( "JPG images only supported in python 2.7+: " + image) else: # read the image into an array of bytes with open(image, 'rb') as inFile: buf = array.array(str('B'), inFile.read()) # init the translator, and decode the array of bytes nanojpeg.njInit() nanojpeg.njDecode(buf, len(buf)) # determine a file name & type if nanojpeg.njIsColor(): # fileName = image.split('.jpg', 1)[0] + '.ppm' param = 6 else: # fileName = image.split('.jpg', 1)[0] + '.pgm' # fileName = "test3.pgm" param = 5 # create a string, starting with the header val = "P%d\n%d %d\n255\n" % ( param, nanojpeg.njGetWidth(), nanojpeg.njGetHeight()) # append the bytes, converted to chars val = str(val) + str('').join(map(chr, nanojpeg.njGetImage())) # release any stuff nanojpeg.njDone() photo = PhotoImage(data=val) return photo # write the chars to a new file, if python3 we need to encode them first # with open(fileName, "wb") as outFile: # if sys.version_info[0] == 2: outFile.write(val) # else: outFile.write(val.encode('ISO-8859-1')) # # return fileName # function to set a background image # make sure this is done before everything else, otherwise it will cover # other widgets def setBgImage(self, image): image = self._getImage(image, False, False) # make sure it's not using the cache # self.containerStack[0]['container'].config(image=image) # window as a # label doesn't work... self.bgLabel.config(image=image) self.containerStack[0]['container'].image = image # keep a reference! def removeBgImage(self): self.bgLabel.config(image="") # self.containerStack[0]['container'].config(image=None) # window as a # label doesn't work... # remove the reference - shouldn't be cached self.containerStack[0]['container'].image = None def resizeBgImage(self): if self.containerStack[0]['container'].image is None: return else: pass ##################################### # FUNCTION to play sounds ##################################### # function to set a sound location def setSoundLocation(self, location): if os.path.isdir(location): self.userSounds = location else: raise Exception("Invalid sound location: " + location) # internal function to manage sound availability def _soundWrap(self, sound, isFile=False, repeat=False, wait=False): self._loadWinsound() if self.platform == self.WINDOWS and winsound is not False: sound = self._translateSound(sound) if self.userSounds is not None and sound is not None: sound = os.path.join(self.userSounds, sound) if isFile: if os.path.isfile(sound) is False: raise Exception("Can't find sound: " + sound) if not sound.lower().endswith('.wav'): raise Exception("Invalid sound format: " + sound) kind = winsound.SND_FILENAME if not wait: kind = kind | winsound.SND_ASYNC else: if sound is None: kind = winsound.SND_FILENAME else: kind = winsound.SND_ALIAS if not wait: kind = kind | winsound.SND_ASYNC if repeat: kind = kind | winsound.SND_LOOP winsound.PlaySound(sound, kind) else: # sound not available at this time raise Exception( "Sound not supported on this platform: " + platform()) def playSound(self, sound, wait=False): self._soundWrap(sound, True, False, wait) def stopSound(self): self._soundWrap(None) def loopSound(self, sound): self._soundWrap(sound, True, True) def soundError(self): self._soundWrap("SystemHand") def soundWarning(self): self._soundWrap("SystemAsterisk") def bell(self): self.containerStack[0]['container'].bell() def playNote(self, note, duration=200): self._loadWinsound() if self.platform == self.WINDOWS and winsound is not False: try: if isinstance(note, UNIVERSAL_STRING): freq = self.NOTES[note.lower()] else: freq = note except KeyError: raise Exception("Error: cannot play note - " + note) try: if isinstance(duration, UNIVERSAL_STRING): length = self.DURATIONS[duration.upper()] else: length = duration except KeyError: raise Exception("Error: cannot play duration - " + duration) try: winsound.Beep(freq, length) except RuntimeError: raise Exception( "Sound not available on this platform: " + platform()) else: # sound not available at this time raise Exception( "Sound not supported on this platform: " + platform()) ##################################### # FUNCTION for radio buttons ##################################### def radio(self, title, name=None, *args, **kwargs): """ simpleGUI - shortner for radioButton() """ return self.radioButton(title, name, *args, **kwargs) def radioButton(self, title, name=None, *args, **kwargs): """ simpleGUI - adds, sets & gets radioButtons all in one go """ widgKind = WIDGET_NAMES.RadioButton selected = kwargs.pop("selected", False) callFunction = kwargs.pop("callFunction", True) change = kwargs.pop("change", None) kind = kwargs.pop('kind', 'standard') # need slightly different approach, as use two params if name is None: return self.getRadioButton(title) # no name = get else: ident = title + "-" + name try: self.widgetManager.verify(widgKind, ident) except: self.setRadioButton(title, name, callFunction=callFunction) rb = self.getRadioButton(title) selected = False else: kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) rb = self._radioButtonMaker(title, name, *args, **kwargs) if selected: self.setRadioButton(title, name) if change is not None: self.setRadioButtonChangeFunction(title, change) if kind == "square": if self.platform == self.MAC: gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title) elif not self.ttkFlag: rb.config(indicatoron=0) else: gui.warn("Square radiobuttons not available in ttk, for radiobutton %s", title) if len(kwargs) > 0: self._configWidget(ident, widgKind, **kwargs) return rb def _radioButtonMaker(self, title, name, row=None, column=0, colspan=0, rowspan=0, **kwargs): return self.addRadioButton(title, name, row, column, colspan, rowspan) def addRadioButton(self, title, name, row=None, column=0, colspan=0, rowspan=0): ''' adds a radio button, to thr group 'title' with the text 'name' ''' ident = title + "-" + name self.widgetManager.verify(WIDGET_NAMES.RadioButton, ident) var = None newRb = False # title - is the grouper # so, if we already have an entry in n_rbVars - get it if (title in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS)): var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS) else: # if this is a new grouper - set it all up var = StringVar(self.topLevel) self.widgetManager.add(WIDGET_NAMES.RadioButton, title, var, group=WidgetManager.VARS) newRb = True # finally, create the actual RadioButton if not self.ttkFlag: rb = Radiobutton(self.getContainer(), text=name, variable=var, value=name) rb.config(anchor=W, background=self._getContainerBg(), indicatoron=1, activebackground=self._getContainerBg(), font=self._getContainerProperty('labelFont') ) else: rb = ttk.Radiobutton(self.getContainer(), text=name, variable=var, value=name) rb.bind("<Button-1>", self._grabFocus) rb.DEFAULT_TEXT = name self.widgetManager.add(WIDGET_NAMES.RadioButton, ident, rb) #rb.bind("<Tab>", self._focusNextWindow) #rb.bind("<Shift-Tab>", self._focusLastWindow) # and select it, if it's the first item in the list if newRb: rb.select() if not self.ttkFlag else rb.invoke() var.startVal = name # so we can reset it... self._positionWidget(rb, row, column, colspan, rowspan, EW) return rb def getRadioButton(self, title): var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS) return var.get() def getAllRadioButtons(self): rbs = {} for k in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS): rbs[k] = self.getRadioButton(k) return rbs def setRadioButton(self, title, value, callFunction=True): ident = title + "-" + value self.widgetManager.get(WIDGET_NAMES.RadioButton, ident) # now call function var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS) with PauseCallFunction(callFunction, var, False): var.set(value) def clearAllRadioButtons(self, callFunction=False): for rb in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS): self.setRadioButton(rb, self.widgetManager.get(WIDGET_NAMES.RadioButton, rb, group=WidgetManager.VARS).startVal, callFunction=callFunction) def setRadioTick(self, title, tick=True): self.warn("Deprecated function (%s) used for %s -> %s use %s instead", 'setRadioTick', 'radioButton', title, 'setRadioSquare') self.setRadioSquare(title, square=tick) def setRadioSquare(self, title, square=True): if self.platform == self.MAC: gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title) elif not self.ttkFlag: for k, v in self.widgetManager.group(WIDGET_NAMES.RadioButton).items(): if k.startswith(title+"-"): if square: v.config(indicatoron=1) else: v.config(indicatoron=0) else: gui.warn("Square radiobuttons not available in ttk mode, for radiobutton %s", title) ##################################### # FUNCTION for list box ##################################### def listbox(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for listBox() """ return self.listBox(title, value, *args, **kwargs) def listBox(self, title, value=None, *args, **kwargs): """ simpleGUI -- adds, sets & gets listBoxes all in one go """ widgKind = WIDGET_NAMES.ListBox rows = kwargs.pop("rows", None) multi = kwargs.pop("multi", False) group = kwargs.pop("group", False) selected = kwargs.pop("selected", None) first = kwargs.pop("first", False) callFunction = kwargs.pop("callFunction", True) # select=select, deselect=??, toggle=??, clear=??, rename=set, replace=update, delete=remove if value is None: mode = 'get' else: mode = 'select' mode = kwargs.pop("mode", mode) try: self.widgetManager.verify(widgKind, title) except: # widget exists if mode == "select": if value is not None: if isinstance(value, int): self.selectListItemAtPos(title, value, *args, **kwargs) else: self.selectListItem(title, value, *args, **kwargs) else: gui.error("No item specified to select in listbox: %s", title) elif mode == "deselect": if value is not None: if isinstance(value, int): self.deselectListItemAtPos(title, value, *args, **kwargs) else: self.deselectListItem(title, value, *args, **kwargs) else: gui.error("No item specified to deselect in listbox: %s", title) elif mode == "toggle": gui.error("%s not implemented yet in listbox: %s", mode, title) elif mode == "clear": self.deselectAllListItems(title) elif mode == "rename": gui.error("%s not implemented yet in listbox: %s", mode, title) elif mode == "replace": if value is not None: self.updateListBox(title, items=value, callFunction=callFunction) else: gui.error("No values specified to replace in listbox: %s", title) elif mode == "delete": if value is not None: if isinstance(value, int): self.removeListItemAtPos(title, value) else: self.removeListItem(title, value) else: gui.error("No value specified to delete in listbox: %s", title) elif mode == "add": if value is not None: select = True if selected is None else selected if type(value) in (list, tuple): self.addListItems(title, items=value, select=select) else: self.addListItem(title, item=value, select=select) else: gui.error("No value specified to add in listbox: %s", title) elif mode == "get": pass else: gui.error("Invalid mode (%s) specified in listbox: %s", mode, title) listBox = self.getListBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) listBox = self._listBoxMaker(title, value, *args, **kwargs) if rows is not None: self.setListBoxRows(title, rows) if multi: self.setListBoxMulti(title) if group: self.setListBoxGroup(title) if selected is not None: self.selectListItemAtPos(title, selected, callFunction=False) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return listBox def _listBoxMaker(self, name, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs): """ internal wrapper to hide kwargs from original add functions """ return self.addListBox(name, values, row, column, colspan, rowspan) def addListBox(self, name, values=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a list box, with the the specified list of values ''' self.widgetManager.verify(WIDGET_NAMES.ListBox, name) container = self.makeListBoxContainer()(self.getContainer()) vscrollbar = AutoScrollbar(container) hscrollbar = AutoScrollbar(container, orient=HORIZONTAL) container.lb = Listbox(container, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set) vscrollbar.grid(row=0, column=1, sticky=N + S) hscrollbar.grid(row=1, column=0, sticky=E + W) container.lb.grid(row=0, column=0, sticky=N + S + E + W) container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) vscrollbar.config(command=container.lb.yview) hscrollbar.config(command=container.lb.xview) container.lb.config(font=self._getContainerProperty('inputFont')) self.widgetManager.add(WIDGET_NAMES.ListBox, name, container.lb) container.lb.DEFAULT_TEXT="" if values is not None: container.lb.DEFAULT_TEXT='\n'.join(str(x) for x in values) for name in values: container.lb.insert(END, name) self._positionWidget(container, row, column, colspan, rowspan) return container.lb # enable multiple listboxes to be selected at the same time def setListBoxGroup(self, name, group=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name) group = not group lb.config(exportselection=group) # set how many rows to display def setListBoxRows(self, name, rows): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name) lb.config(height=rows) # make the list single/multi select # default is single def setListBoxMulti(self, title, multi=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if multi: lb.config(selectmode=EXTENDED) else: lb.config(selectmode=BROWSE) # select the specified item in the list def selectListItem(self, title, item, callFunction=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) positions = self._getListPositions(title, item) if len(positions) > 1 and lb.cget("selectmode") == EXTENDED: allOk = True for pos in positions: if not self.selectListItemAtPos(title, pos, callFunction): allOk = False return allOk elif len(positions) > 1: gui.warn("Unable to select multiple items for list: %s. Selecting first item: %s", title, item[0]) return self.selectListItemAtPos(title, positions[0], callFunction) elif len(positions) == 1: return self.selectListItemAtPos(title, positions[0], callFunction) else: gui.warn("Invalid list item(s): %s for list: %s", item, title) return False def deselectListItemAtPos(self, title, pos, callFunction=False): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if lb.size() == 0: gui.warn("No items in list: %s, unable to deselect item at pos: %s", title, pos) return False if pos < 0 or pos > lb.size() - 1: gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1) return False lb.selection_clear(pos) if callFunction and hasattr(lb, 'cmd'): lb.cmd() self.topLevel.update_idletasks() return True def selectListItemAtPos(self, title, pos, callFunction=False): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if lb.size() == 0: gui.warn("No items in list: %s, unable to select item at pos: %s", title, pos) return False if pos < 0 or pos > lb.size() - 1: gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1) return False # clear previous selection if we're not multi if lb.cget("selectmode") != EXTENDED: lb.selection_clear(0, END) # show & select this item lb.see(pos) lb.activate(pos) lb.selection_set(pos) # now call function if callFunction and hasattr(lb, 'cmd'): lb.cmd() self.topLevel.update_idletasks() return True # replace the list items in the list box def updateListBox(self, title, items, select=False, callFunction=True): self.clearListBox(title, callFunction=callFunction) self.addListItems(title, items, select=select) def addListItems(self, title, items, select=True): ''' adds the list of items to the specified list box ''' for i in items: self.addListItem(title, i, select=select) def addListItem(self, title, item, pos=None, select=True): ''' add the item to the end of the specified list box ''' lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) # add it at the end if pos is None: pos = END lb.insert(pos, item) # show & select the newly added item if select: # clear any selection items = lb.curselection() if len(items) > 0: lb.selection_clear(items) self.selectListItemAtPos(title, lb.size() - 1) def deselectAllListItems(self, title, callFunction=False): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.selection_clear(0, END) if callFunction and hasattr(lb, 'cmd'): lb.cmd() # returns a list containing 0 or more elements # all that are in the selected range def getListBox(self, title): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) items = lb.curselection() values = [] for loop in range(len(items)): values.append(lb.get(items[loop])) return values def getAllListBoxes(self): boxes = {} for k in self.widgetManager.group(WIDGET_NAMES.ListBox): boxes[k] = self.getListBox(k) return boxes def getAllListItems(self, title): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) items = lb.get(0, END) return list(items) def getListBoxPos(self, title): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) # bug in tkinter 1.160 returns these as strings items = [int(i) for i in lb.curselection()] return items def removeListItemAtPos(self, title, pos): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) items = lb.get(0, END) if pos >= len(items): raise Exception("Invalid position: " + str(pos) + " must be between 0 and " + str(len(items)-1)) lb.delete(pos) # show & select this item if pos >= lb.size(): pos -= 1 self.selectListItemAtPos(title, pos) # remove a specific item from the listBox # will only remove the first item that matches the String def removeListItem(self, title, item): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) positions = self._getListPositions(title, item) if len(positions) > 0: lb.delete(positions[0]) # show & select this item if positions[0] >= lb.size(): positions[0] -= 1 self.selectListItemAtPos(title, positions[0]) def setListItemAtPos(self, title, pos, newVal): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.delete(pos) lb.insert(pos, newVal) def setListItem(self, title, item, newVal, first=False): for pos in self._getListPositions(title, item): self.setListItemAtPos(title, pos, newVal) if first: break # functions to config def setListItemAtPosBg(self, title, pos, col): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.itemconfig(pos, bg=col) def setListItemAtPosFg(self, title, pos, col): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.itemconfig(pos, fg=col) def _getListPositions(self, title, item): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if not isinstance(item, list): item = [item] vals = lb.get(0, END) positions = [] for pos, val in enumerate(vals): if val in item: positions.append(pos) return positions def setListItemBg(self, title, item, col): for pos in self._getListPositions(title, item): self.setListItemAtPosBg(title, pos, col) def setListItemFg(self, title, item, col): for pos in self._getListPositions(title, item): self.setListItemAtPosFg(title, pos, col) def clearListBox(self, title, callFunction=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.selection_clear(0, END) lb.delete(0, END) # clear if callFunction and hasattr(lb, 'cmd'): lb.cmd() def clearAllListBoxes(self, callFunction=False): for lb in self.widgetManager.group(WIDGET_NAMES.ListBox): self.clearListBox(lb, callFunction) ##################################### # FUNCTION for buttons ##################################### def button(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets buttons all in one go """ widgKind = WIDGET_NAMES.Button image = kwargs.pop("image", None) icon = kwargs.pop("icon", None) name = kwargs.pop("label", kwargs.pop("name", None)) try: self.widgetManager.verify(WIDGET_NAMES.Button, title) except: # widget exists if value is not None: self.setButton(title, value) button = self.getButton(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if image is not None: button = self._buttonMaker(title, value, "image", image, *args, **kwargs) elif icon is not None: button = self._buttonMaker(title, value, "icon", icon, *args, **kwargs) elif name is not None: button = self._buttonMaker(title, value, "named", name, *args, **kwargs) else: button = self._buttonMaker(title, value, "button", None, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return button def _buttonMaker(self, title, func, kind, extra=None, row=None, column=0, colspan=0, rowspan=0, *args, **kwargs): """ internal wrapper to hide kwargs from original add functions """ align = kwargs.pop("align", None) if kind == "button": return self.addButton(title, func, row, column, colspan, rowspan) elif kind == "named": return self.addNamedButton(extra, title, func, row, column, colspan, rowspan) elif kind == "image": return self.addImageButton(title, func, extra, row, column, colspan, rowspan, align=align) elif kind == "icon": return self.addIconButton(title, func, extra, row, column, colspan, rowspan, align=align) def _configWidget(self, title, kind, **kwargs): widget = self.widgetManager.get(kind, title) # remove any unwanted keys for key in ["row", "column", "colspan", "rowspan", "label", "name"]: kwargs.pop(key, None) # ignore these for now as well for key in ["pad", "inpad"]: val = kwargs.pop(key, None) if val is not None: gui.error("Invalid argument for %s %s - %s:%s", WIDGET_NAMES.name(kind), title, key, val) tooltip = kwargs.pop("tip", kwargs.pop("tooltip", None)) change = kwargs.pop("change", None) submit = kwargs.pop("submit", None) over = kwargs.pop("over", None) drag = kwargs.pop("drag", None) drop = kwargs.pop("drop", None) right = kwargs.pop("right", None) focus = kwargs.pop('focus', False) _font = kwargs.pop('font', None) if tooltip is not None: self._addTooltip(widget, tooltip, None) if focus: widget.focus_set() if change is not None: self._bindEvent(kind, title, widget, change, "change", key=None) if submit is not None: self._bindEvent(kind, title, widget, submit, "submit", key=None) if over is not None: self._bindOverEvent(kind, title, widget, over, None, None) if drag is not None: self._bindDragEvent(kind, title, widget, drag, None, None) if drop is not None: self._registerExternalDropTarget(title, widget, drop) if right is not None: self._bindRightClick(widget, right) # allow fonts to be passed in as either a dictionary or a single integer or a font object if _font is not None: if isinstance(_font, tkFont.Font): widget.config(font=_font) else: if not isinstance(_font, dict): #Â assume int _font = {"size":_font} custFont = tkFont.Font(**_font) widget.config(font=custFont) # now pass the kwargs to the config function, ignore any baddies errorMsg = "" while True: try: widget.config(**kwargs) except TclError as e: try: key=str(e).split()[2][2:-1] errorMsg = "".join([errorMsg, key, ":", kwargs.pop(key), ", "]) except: gui.error("Invalid argument for %s %s: %s", WIDGET_NAMES.name(kind), title, e) break else: break if len(errorMsg) > 0: gui.error("Invalid arguments for %s %s - %s", WIDGET_NAMES.name(kind), title, errorMsg) def _buildButton(self, title, func, frame, name=None): if name is None: name = title if isinstance(title, list): raise Exception("Can't add a button using a list of names: " + str(title) + " - you should use .addButtons()") self.widgetManager.verify(WIDGET_NAMES.Button, title) if not self.ttkFlag: but = Button(frame, text=name) but.config(font=self._getContainerProperty('buttonFont')) if self.platform in [self.MAC, self.LINUX]: but.config(highlightbackground=self._getContainerBg()) else: but = ttk.Button(frame, text=name) but.DEFAULT_TEXT = name if func is not None: command = self.MAKE_FUNC(func, title) but.config(command=command) #but.bind("<Tab>", self._focusNextWindow) #but.bind("<Shift-Tab>", self._focusLastWindow) self.widgetManager.add(WIDGET_NAMES.Button, title, but) return but def addNamedButton(self, name, title, func, row=None, column=0, colspan=0, rowspan=0): ''' adds a button, displaying the name as its text ''' but = self._buildButton(title, func, self.getContainer(), name) self._positionWidget(but, row, column, colspan, rowspan, None) return but def addButton(self, title, func, row=None, column=0, colspan=0, rowspan=0): ''' adds a button with the title as its text ''' but = self._buildButton(title, func, self.getContainer()) self._positionWidget(but, row, column, colspan, rowspan, None) return but def addImageButton(self, title, func, imgFile, row=None, column=0, colspan=0, rowspan=0, align=None): ''' adds a button, displaying the specified image file ''' but = self._buildButton(title, func, self.getContainer()) self._positionWidget(but, row, column, colspan, rowspan, None) self.setButtonImage(title, imgFile, align) return but def addIconButton(self, title, func, iconName, row=None, column=0, colspan=0, rowspan=0, align=None): ''' adds a button displaying the specified icon ''' icon = os.path.join(self.icon_path, iconName.lower()+".png") with PauseLogger(): return self.addImageButton(title, func, icon, row, column, colspan, rowspan, align) def setButton(self, name, text): but = self.widgetManager.get(WIDGET_NAMES.Button, name) try: # try to bind a function command = self.MAKE_FUNC(text, name) but.config(command=command) except: # otherwise change the text but.config(text=text) def getButton(self, name): but = self.widgetManager.get(WIDGET_NAMES.Button, name) return but.cget("text") def setButtonImage(self, name, imgFile, align=None): but = self.widgetManager.get(WIDGET_NAMES.Button, name) image = self._getImage(imgFile) # works on Mac & Windows :) if align == None: but.config(image=image, text="") if not self.ttk: but.config(justify=LEFT, compound=TOP) else: but.config(compound=CENTER) else: but.config(image=image, compound=align) # but.config(image=image, compound=None, text="") # works on Windows, not Mac but.image = image # adds a set of buttons, in the row, spannning specified columns # pass in a list of names & a list of functions (or a single function to # use for all) def buttons(self, names, funcs, **kwargs): kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) self._addButtons(names, funcs, **kwargs) kwargs.pop('fill', False) if not isinstance(names[0], list): names = [names] for row in names: for title in row: self._configWidget(title, WIDGET_NAMES.Button, **kwargs) def _addButtons(self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False, **kwargs): self.addButtons(names, funcs, row, column, colspan, rowspan, fill) def addButtons(self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False): ''' adds a 1D/2D list of buttons ''' if not isinstance(names, list): raise Exception( "Invalid button: " + names + ". It must be a list of buttons.") singleFunc = self._checkFunc(names, funcs) frame = self._makeWidgetBox()(self.getContainer()) if not self.ttk: frame.config(background=self._getContainerBg()) # make them into a 2D array, if not already if not isinstance(names[0], list): names = [names] # won't be used if single func if funcs is not None: funcs = [funcs] sticky = None if fill: sticky=E+W for bRow in range(len(names)): for i in range(len(names[bRow])): t = names[bRow][i] if funcs is None: tempFunc = None elif singleFunc is None: tempFunc = funcs[bRow][i] else: tempFunc = singleFunc but = self._buildButton(t, tempFunc, frame) but.grid(row=bRow, column=i, sticky=sticky) Grid.columnconfigure(frame, i, weight=1) Grid.rowconfigure(frame, bRow, weight=1) frame.theWidgets.append(but) self._positionWidget(frame, row, column, colspan, rowspan) self.widgetManager.log(WIDGET_NAMES.FrameBox, frame) ##################################### # FUNCTIONS for links ##################################### def link(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets links all in one go """ widgKind = WIDGET_NAMES.Link try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: self.setLink(title, value) link = self.getLink(title) else: # new widget if value is None: gui.warn("Can't create link: %s, with no value", title) return None kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) link = self._linkMaker(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return link def _linkMaker(self, title, value, row=None, column=0, colspan=0, rowspan=0, *args, **kwargs): if not callable(value) and not hasattr(value, '__call__'): return self.addWebLink(title, value, row, column, colspan, rowspan) else: return self.addLink(title, value, row, column, colspan, rowspan) def _buildLink(self, title): self._importWebBrowser() if not webbrowser: self.error("Unable to load webbrowser - can't create links") link = self._makeLink()(self.getContainer(), useTtk=self.ttkFlag) link.config(text=title, font=self._linkFont) if not self.ttk: link.config(background=self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.Link, title, link) return link # launches a browser to the specified page def addWebLink(self, title, page, row=None, column=0, colspan=0, rowspan=0): ''' adds a hyperlink to the specified web page ''' link = self._buildLink(title) link.registerWebpage(page) self._positionWidget(link, row, column, colspan, rowspan) return link # executes the specified function def addLink(self, title, func, row=None, column=0, colspan=0, rowspan=0): ''' adds a hyperlink to the specified function ''' link = self._buildLink(title) if func is not None: myF = self.MAKE_FUNC(func, title) link.registerCallback(myF) self._positionWidget(link, row, column, colspan, rowspan) return link def getLink(self, title): link = self.widgetManager.get(WIDGET_NAMES.Link, title) return link.cget("text") def setLink(self, title, func): link = self.widgetManager.get(WIDGET_NAMES.Link, title) if not callable(func) and not hasattr(func, '__call__'): link.registerWebpage(func) else: myF = self.MAKE_FUNC(func, title) link.registerCallback(myF) ##################################### # FUNCTIONS for grips ##################################### def grip(self, *args, **kwargs): """ simpleGUI - adds grip """ kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) return self.addGrip(*args, **kwargs) # adds a simple grip, used to drag the window around def addGrip(self, row=None, column=0, colspan=0, rowspan=0): ''' adds a grip, for dragging the GUI around ''' grip = self._makeGrip()(self.getContainer()) self._positionWidget(grip, row, column, colspan, rowspan) self._addTooltip(grip, "Drag here to move", True) return grip ##################################### # FUNCTIONS for dnd ##################################### def addTrashBin(self, title, row=None, column=0, colspan=0, rowspan=0): ''' NOT IN USE - adds a trashbin, for discarding dragged items ''' trash = TrashBin(self.getContainer()) self._positionWidget(trash, row, column, colspan, rowspan) return trash ##################################### # FUNCTIONS for turtle ##################################### def addTurtle(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a turtle widget at the specified position ''' self._loadTurtle() if turtle is False: raise Exception("Unable to load turtle") self.widgetManager.verify(WIDGET_NAMES.Turtle, title) canvas = Canvas(self.getContainer()) canvas.screen = turtle.TurtleScreen(canvas) self._positionWidget(canvas, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Turtle, title, canvas) canvas.turtle = turtle.RawTurtle(canvas.screen) return canvas.turtle def getTurtleScreen(self, title): return self.widgetManager.get(WIDGET_NAMES.Turtle, title).screen def getTurtle(self, title): return self.widgetManager.get(WIDGET_NAMES.Turtle, title).turtle ##################################### # FUNCTIONS for canvas ##################################### def addCanvas(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a canvas at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.Canvas, title) canvas = Canvas(self.getContainer()) canvas.config(bd=0, highlightthickness=0) canvas.imageStore = [] self._positionWidget(canvas, row, column, colspan, rowspan, "news") self.widgetManager.add(WIDGET_NAMES.Canvas, title, canvas) return canvas def getCanvas(self, title): return self.widgetManager.get(WIDGET_NAMES.Canvas, title) def clearCanvas(self, title): self.widgetManager.get(WIDGET_NAMES.Canvas, title).delete("all") # function to configure a canvas map def setCanvasMap(self, name, func, coords): self._setWidgetMap(name, WIDGET_NAMES.Canvas, func, coords) def addCanvasCircle(self, title, x, y, diameter, **kwargs): ''' adds a circle to the specified canvas ''' return self.addCanvasOval(title, x, y, diameter, diameter, **kwargs) def addCanvasOval(self, title, x, y, xDiam, yDiam, **kwargs): ''' adds a oval to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_oval(x, y, x+xDiam, y+yDiam, **kwargs) def addCanvasLine(self, title, x, y, x2, y2, **kwargs): ''' adds a line to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_line(x, y, x2, y2, **kwargs) def addCanvasRectangle(self, title, x, y, w, h, **kwargs): ''' adds a rectangle to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_rectangle(x, y, x+w, y+h, **kwargs) def addCanvasText(self, title, x, y, text=None, **kwargs): ''' adds text to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_text(x, y, text=text, **kwargs) def addCanvasImage(self, title, x, y, image=image, **kwargs): ''' adds an image to the specified canvas ''' canv = self.widgetManager.get(WIDGET_NAMES.Canvas, title) if isinstance(image, UNIVERSAL_STRING): image = self._getImage(image) canv.imageStore.append(image) return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_image(x, y, image=image, **kwargs) def setCanvasEvent(self, title, item, event, function, add=None): canvas = self.widgetManager.get(WIDGET_NAMES.Canvas, title) canvas.tag_bind(item, event, function, add) def _canvasMaker(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs): return self.addCanvas(title, row, column, rowspan) def canvas(self, title, *args, **kwargs): """ simpleGUI - adds, sets & gets canases all in one go """ widgKind = WIDGET_NAMES.Canvas submit = kwargs.pop("submit", None) _map = kwargs.pop("map", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists # NB. no SETTER canvas = self.getCanvas(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) canvas = self._canvasMaker(title, *args, **kwargs) if submit is not None and _map is not None: self.setCanvasMap(title, submit, _map) else: gui.warn("Must specify a submit function when setting a canvas map: %s", title) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) self._configWidget(title, widgKind, **kwargs) return canvas ##################################### # FUNCTIONS for Microbits ##################################### def microbit(self, title, *args, **kwargs): '''simpleGUI - adds, sets & gets microbits all in one go''' widgKind = WIDGET_NAMES.MicroBit image = kwargs.pop("image", None) brightness = kwargs.pop("brightness", None) x = kwargs.pop("x", None) y = kwargs.pop("y", None) clear = kwargs.pop("clear", False) try: self.widgetManager.verify(widgKind, title) except: # widget exists mb = self.getMicroBit(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) mb = self.addMicroBit(title, *args, **kwargs) if image is not None: self.setMicroBitImage(title, image) if brightness is not None: self.setMicroBitPixel(title, x, y, brightness) if clear: self.clearMicroBit(title) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return mb def addMicroBit(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a simple microbit widget used with permission from Ben Goodwin ''' self.widgetManager.verify(WIDGET_NAMES.MicroBit, title) mb = MicroBitSimulator(self.getContainer()) self._positionWidget(mb, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.MicroBit, title, mb) return mb def setMicroBitImage(self, title, image): self.widgetManager.get(WIDGET_NAMES.MicroBit, title).show(image) def setMicroBitPixel(self, title, x, y, brightness): self.widgetManager.get(WIDGET_NAMES.MicroBit, title).set_pixel(x, y, brightness) def clearMicroBit(self, title): self.widgetManager.get(WIDGET_NAMES.MicroBit, title).clear() ##################################### # DatePicker Widget - using Form Container ##################################### def date(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for datePicker() """ return self.datePicker(title, value, *args, **kwargs) def datePicker(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets datePickers all in one go """ widgKind = WIDGET_NAMES.DatePicker change = kwargs.pop("change", None) toValue = kwargs.pop("toValue", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists dp = self.getDatePicker(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) dp = self.addDatePicker(title, *args, **kwargs) if value is not None: if toValue is None: self.setDatePicker(title, value) else: self.setDatePickerRange(title, startYear=value, endYear=toValue) if change is not None: self.setDatePickerChangeFunction(title, change) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return dp def addDatePicker(self, name, row=None, column=0, colspan=0, rowspan=0): ''' adds a date picker at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.DatePicker, name) # initial DatePicker has these dates days = range(1, 32) self.MONTH_NAMES = calendar.month_name[1:] years = range(1970, 2021) # create a frame, and add the widgets frame = self.startFrame(name, row, column, colspan, rowspan) self.setExpand("none") self.addLabel(name + "_DP_DayLabel", "Day:", 0, 0) self.setLabelAlign(name + "_DP_DayLabel", "w") self.addOptionBox(name + "_DP_DayOptionBox", days, 0, 1) self.addLabel(name + "_DP_MonthLabel", "Month:", 1, 0) self.setLabelAlign(name + "_DP_MonthLabel", "w") self.addOptionBox(name + "_DP_MonthOptionBox", self.MONTH_NAMES, 1, 1) self.addLabel(name + "_DP_YearLabel", "Year:", 2, 0) self.setLabelAlign(name + "_DP_YearLabel", "w") self.addOptionBox(name + "_DP_YearOptionBox", years, 2, 1) self.setOptionBoxChangeFunction( name + "_DP_MonthOptionBox", self._updateDatePickerDays) self.setOptionBoxChangeFunction( name + "_DP_YearOptionBox", self._updateDatePickerDays) self.stopFrame() frame.isContainer = False self.widgetManager.add(WIDGET_NAMES.DatePicker, name, frame) def setDatePickerFg(self, name, fg): self.widgetManager.get(WIDGET_NAMES.DatePicker, name) self.setLabelFg(name + "_DP_DayLabel", fg) self.setLabelFg(name + "_DP_MonthLabel", fg) self.setLabelFg(name + "_DP_YearLabel", fg) def setDatePickerChangeFunction(self, title, function): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) cmd = self.MAKE_FUNC(function, title) self.setOptionBoxChangeFunction(title + "_DP_DayOptionBox", cmd) self.widgetManager.get(WIDGET_NAMES.OptionBox, title + "_DP_DayOptionBox").function = cmd # function to update DatePicker dropDowns def _updateDatePickerDays(self, title): if title.find("_DP_MonthOptionBox") > -1: title = title.split("_DP_MonthOptionBox")[0] elif title.find("_DP_YearOptionBox") > -1: title = title.split("_DP_YearOptionBox")[0] else: self.warn("Can't update days in DatePicker:%s", title) return day = self.getOptionBox(title + "_DP_DayOptionBox") month = self.MONTH_NAMES.index(self.getOptionBox(title + "_DP_MonthOptionBox")) + 1 year = int(self.getOptionBox(title + "_DP_YearOptionBox")) days = range(1, calendar.monthrange(year, month)[1] + 1) self.changeOptionBox(title + "_DP_DayOptionBox", days) # keep previous day if possible with PauseLogger(): self.setOptionBox(title + "_DP_DayOptionBox", day, callFunction=False) box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title + "_DP_DayOptionBox") if hasattr(box, 'function'): box.function() # set a date for the named DatePicker def setDatePickerRange(self, title, startYear, endYear=None): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) if endYear is None: endYear = datetime.date.today().year years = range(startYear, endYear + 1) self.changeOptionBox(title + "_DP_YearOptionBox", years) def setDatePicker(self, title, date="today"): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) if date == "today": date = datetime.date.today() self.setOptionBox(title + "_DP_YearOptionBox", str(date.year)) self.setOptionBox(title + "_DP_MonthOptionBox", date.month - 1) self.setOptionBox(title + "_DP_DayOptionBox", date.day - 1) def clearDatePicker(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) self.setOptionBox(title + "_DP_YearOptionBox", 0, callFunction) self.setOptionBox(title + "_DP_MonthOptionBox", 0, callFunction) self.setOptionBox(title + "_DP_DayOptionBox", 0, callFunction) def clearAllDatePickers(self, callFunction=False): for k in self.widgetManager.group(WIDGET_NAMES.DatePicker): self.clearDatePicker(k, callFunction) def getDatePicker(self, title): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) day = int(self.getOptionBox(title + "_DP_DayOptionBox")) month = self.MONTH_NAMES.index( self.getOptionBox( title + "_DP_MonthOptionBox")) + 1 year = int(self.getOptionBox(title + "_DP_YearOptionBox")) date = datetime.date(year, month, day) return date def getAllDatePickers(self): dps = {} for k in self.widgetManager.group(WIDGET_NAMES.DatePicker): dps[k] = self.getDatePicker(k) return dps ##################################### # FUNCTIONS for ACCESSABILITY ##################################### def _makeAccess(self): if not self.accessMade: def _close(): self.hideSubWindow("access_access_subwindow") def _changeFg(): self.label("access_fg_colBox", bg=self.colourBox(self.getLabelBg("access_fg_colBox"))) def _changeBg(): self.label("access_bg_colBox", bg=self.colourBox(self.getLabelBg("access_bg_colBox"))) def _settings(): font = {"underline":self.check("access_underline_check"), "overstrike":self.check("access_overstrike_check")} font["weight"] = "bold" if self.check("access_bold_check") is True else "normal" font["slant"] = "roman" if self.radio("access_italic_radio") == "Normal" else "italic" if len(self.listbox("access_family_listbox")) > 0: font["family"] = self.listbox("access_family_listbox")[0] if self.option("access_size_option") is not None: font["size"] = self.option("access_size_option") if self.check('access_label_check'): self.labelFont = font if self.check('access_input_check'): self.inputFont = font if self.check('access_button_check'): self.buttonFont = font self.bg = self.getLabelBg("access_bg_colBox") self.fg = self.getLabelBg("access_fg_colBox") self.accessOrigFont = self.accessOrigBg = self.accessOrigFg = None with self.subWindow("access_access_subwindow", sticky = "news", title="Accessibility", resizable=False) as sw: if not self.ttk: sw.config(padx=5, pady=1) with self.labelFrame("access_font_labelframe", sticky="news", name="Font") as lf: if not self.ttk: lf.config(padx=5, pady=5, font=self._accessFont) with self.frame("access_ticks_frame", colspan=2): self.check("access_label_check", True, label="Labels", pos=(0,0), font=self._accessFont, tip="Set label fonts") self.check("access_input_check", label="Inputs", pos=(0,1), font=self._accessFont, tip="Set input fonts") self.check("access_button_check", label="Buttons", pos=(0,2), font=self._accessFont, tip="Set button fonts") self.listbox("access_family_listbox", self.fonts, rows=6, tip="Choose a font", colspan=2, font=self._accessFont) self.option("access_size_option", [7, 8, 9, 10, 12, 13, 14, 16, 18, 20, 22, 25, 29, 34, 40], label="Size:", tip="Choose a font size", font=self._accessFont) self.check("access_bold_check", name="Bold", pos=('p',1), tip="Check this to make all font bold", font=self._accessFont) self.radio("access_italic_radio", "Normal", tip="No italics", font=self._accessFont) self.radio("access_italic_radio", "Italic", pos=('p',1), tip="Set font italic", font=self._accessFont) self.check("access_underline_check", name="Underline", tip="Underline all text", font=self._accessFont) self.check("access_overstrike_check", name="Overstrike", pos=('p',1), tip="Strike out all text", font=self._accessFont) with self.labelFrame("access_colour_labelframe", sticky="news", name="Colours") as lf: if not self.ttk: lf.config(padx=5, pady=5, font=self._accessFont) self.label("access_fg_text", "Foreground:", sticky="ew", anchor="w", font=self._accessFont) self.label("access_fg_colBox", "", pos=('p',1), sticky="ew", submit=_changeFg, relief="ridge", tip="Click here to set the foreground colour", font=self._accessFont, width=14) self.label("access_bg_text", "Background:", sticky="ew", anchor="w", font=self._accessFont) self.label("access_bg_colBox", "", pos=('p',1), sticky="ew", submit=_changeBg, relief="ridge", tip="Click here to set the background colour", font=self._accessFont, width=14) self.sticky="se" with self.frame("access_button_box"): self.button("access_apply_button", _settings, name="Apply", pos=(0,0), font=self._accessFont) self.button("access_reset_button", self._resetAccess, name="Reset", pos=(0,1), font=self._accessFont) self.button("access_close_button", _close, name="Close", pos=(0,2), font=self._accessFont) self.accessMade = True def _resetAccess(self): if self.accessMade: self.check("access_label_check", True) self.check("access_input_check", False) self.check("access_button_check", False) self.listbox("access_family_listbox", self.accessOrigFont["family"]) self.option("access_size_option", str(self.accessOrigFont["size"])) if self.accessOrigFont["weight"] == "normal": self.check("access_bold_check", False) else: self.check("access_bold_check", True) if self.accessOrigFont["slant"] == "roman": self.radio("access_italic_radio", "Normal") else: self.radio("access_italic_radio", "Italic") self.check("access_overstrike_check", self.accessOrigFont["overstrike"]) self.check("access_underline_check", self.accessOrigFont["underline"]) self.label("access_fg_colBox", bg=self.accessOrigFg) self.label("access_bg_colBox", bg=self.accessOrigBg) else: gui.warn("Accessibility not set up yet.") def showAccess(self, location=None): self._makeAccess() # update current settings self.accessOrigFont = self.font self.accessOrigBg = self.bg self.accessOrigFg = self.fg self._resetAccess() self.showSubWindow("access_access_subwindow") ##################################### # FUNCTIONS for labels ##################################### def _parsePos(self, pos, kwargs): # alternative for specifying position if type(pos) != list and type(pos) != tuple: pos = (pos,) if len(pos) > 0: kwargs["row"] = pos[0] if len(pos) > 1: kwargs["column"] = pos[1] if len(pos) > 2: kwargs["colspan"] = pos[2] if len(pos) > 3: kwargs["rowspan"] = pos[3] # allow an alternative kwarg if "col" in kwargs: kwargs["column"]=kwargs.pop("col") # let user specify stickt/stretch/expan sticky = kwargs.pop("sticky", None) if sticky is not None: self.setSticky(sticky) stretch = kwargs.pop("stretch", None) if stretch is not None: self.setStretch(stretch) expand = kwargs.pop("expand", None) if expand is not None: self.setExpand(expand) return kwargs def label(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets labels all in one go """ widgKind = WIDGET_NAMES.Label kind = kwargs.pop("kind", "standard").lower().strip() try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: self.setLabel(title, value) label = self.getLabel(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "flash": label = self._labelMaker(title, value, kind, *args, **kwargs) elif kind == "selectable": label = self._labelMaker(title, value, kind, *args, **kwargs) else: label = self._labelMaker(title, value, "label", *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return label def _labelMaker(self, title, text=None, kind="label", row=None, column=0, colspan=0, rowspan=0, **kwargs): """ Internal wrapper, to hide kwargs from original add functions """ if kind == "flash": return self.addFlashLabel(title, text, row, column, colspan, rowspan) elif kind == "selectable": return self.addSelectableLabel(title, text, row, column, colspan, rowspan) elif kind == "label": return self.addLabel(title, text, row, column, colspan, rowspan) def _flash(self): if not self.alive: return if self.doFlash: for lab in self.widgetManager.group(WIDGET_NAMES.FlashLabel): bg = lab.cget("background") fg = lab.cget("foreground") lab.config(background=fg, foreground=bg) self.flashId = self.topLevel.after(250, self._flash) def addFlashLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a label with flashing text ''' lab = self.addLabel(title, text, row, column, colspan, rowspan) self.widgetManager.log(WIDGET_NAMES.FlashLabel, lab) self.doFlash = True return lab def addSelectableLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a label with selectable text ''' return self.addLabel(title, text, row, column, colspan, rowspan, selectable=True) def addLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0, selectable=False): """Add a label to the GUI. :param title: a unique identifier for the Label :param text: optional text for the Label :param row/column/colspan/rowspan: the row/column to position the label in & how many rows/columns to strecth across :raises ItemLookupError: raised if the title is not unique """ self.widgetManager.verify(WIDGET_NAMES.Label, title) if text is None: gui.trace("Not specifying text for labels (%s) now uses the title for the text. If you want an empty label, pass an empty string ''", title) text = title if not selectable: if not self.ttkFlag: lab = Label(self.getContainer(), text=text) lab.config(justify=LEFT, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) lab.origBg = self._getContainerBg() else: lab = ttk.Label(self.getContainer(), text=text) else: lab = SelectableLabel(self.getContainer(), text=text) lab.config(justify=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) lab.origBg = self._getContainerBg() lab.inContainer = False lab.DEFAULT_TEXT = text self.widgetManager.add(WIDGET_NAMES.Label, title, lab) self._positionWidget(lab, row, column, colspan, rowspan) return lab def addEmptyLabel(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an empty label ''' return self.addLabel(title=title, text='', row=row, column=column, colspan=colspan, rowspan=rowspan) def addLabels(self, names, row=None, colspan=0, rowspan=0): ''' adds a set of labels, in the row, spannning specified columns ''' frame = self._makeWidgetBox()(self.getContainer()) if not self.ttkFlag: frame.config(background=self._getContainerBg()) for i in range(len(names)): self.widgetManager.verify(WIDGET_NAMES.Label, names[i]) if not self.ttkFlag: lab = Label(frame, text=names[i]) lab.config(font=self._getContainerProperty('labelFont'), justify=LEFT, background=self._getContainerBg()) else: lab = ttk.Label(frame, text=names[i]) lab.DEFAULT_TEXT = names[i] lab.inContainer = False self.widgetManager.add(WIDGET_NAMES.Label, names[i], lab) lab.grid(row=0, column=i) Grid.columnconfigure(frame, i, weight=1) Grid.rowconfigure(frame, 0, weight=1) frame.theWidgets.append(lab) self._positionWidget(frame, row, 0, colspan, rowspan) self.widgetManager.log(WIDGET_NAMES.FrameBox, frame) def setLabel(self, name, text): lab = self.widgetManager.get(WIDGET_NAMES.Label, name) lab.config(text=text) def getLabel(self, name): lab = self.widgetManager.get(WIDGET_NAMES.Label, name) return lab.cget("text") def clearLabel(self, name): self.setLabel(name, "") def clearAllLabels(self): for lb in self.widgetManager.group(WIDGET_NAMES.Label): self.clearLabel(lb) ##################################### # FUNCTIONS to add Text Area ##################################### def text(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for textArea() """ return self.textArea(title, value, *args, **kwargs) def textArea(self, title, value=None, *args, **kwargs): """ adds, sets & gets textAreas all in one go """ widgKind = WIDGET_NAMES.TextArea scroll = kwargs.pop("scroll", False) end = kwargs.pop("end", True) replace = kwargs.pop("replace", False) callFunction = kwargs.pop("callFunction", True) disabled = kwargs.pop("disabled", False) tag = kwargs.pop("tag", None) tags = kwargs.pop("tags", []) try: self.widgetManager.verify(WIDGET_NAMES.TextArea, title) except: # widget exists text = self.getTextArea(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if scroll: text = self._textMaker(title, "scroll", *args, **kwargs) else: text = self._textMaker(title, "text", *args, **kwargs) callFunction = False # create any tags for _tag in tags: self.textAreaCreateTag(title, _tag[0], **_tag[1]) if replace: self.clearTextArea(title) if value is not None: self.setTextArea(title, value, end=end, callFunction=callFunction, tag=tag) if disabled: self.disableTextArea(title) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return text def _textMaker(self, title, kind="text", row=None, column=0, colspan=0, rowspan=0, *args, **kwargs): if kind == "scroll": return self.addScrolledTextArea(title, row, column, colspan, rowspan) elif kind == "text": return self.addTextArea(title, row, column, colspan, rowspan) def _buildTextArea(self, title, frame, scrollable=False): """ Internal wrapper, used for building TextAreas. :param title: the key used to reference this TextArea :param frame: this should be a container, used as the parent for the OptionBox :param scrollable: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use """ self.widgetManager.verify(WIDGET_NAMES.TextArea, title) if scrollable: text = AjScrolledText(frame) else: text = AjText(frame) text.config(width=20, height=10, undo=True, wrap=WORD) if not self.ttkFlag: if self.platform in [self.MAC, self.LINUX]: text.config(highlightbackground=self._getContainerBg()) text.bind("<Tab>", self._focusNextWindow) text.bind("<Shift-Tab>", self._focusLastWindow) # add a right click menu text.var = None self._addRightClickMenu(text) self.widgetManager.add(WIDGET_NAMES.TextArea, title, text) self.logTextArea(title) return text def addTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None): """ Adds a TextArea with the specified title Simply calls internal _buildTextArea function before positioning the widget :param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use """ txt = self._buildTextArea(title, self.getContainer()) self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W) if text is not None: self.setTextArea(title, text, callFunction=False) return txt def addScrolledTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None): """ Adds a Scrollable TextArea with the specified title Simply calls internal _buildTextArea functio, specifying a ScrollabelTextArea before positioning the widget :param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use """ txt = self._buildTextArea(title, self.getContainer(), True) self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W) if text is not None: self.setTextArea(title, text, callFunction=False) return txt def getTextArea(self, title): """ Gets the text in the specified TextArea :param title: the TextArea to check :returns: the text in the specified TextArea :raises ItemLookupError: if the title can't be found """ return self.widgetManager.get(WIDGET_NAMES.TextArea, title).getText() def getAllTextAreas(self): """ Convenience function to get the text for all TextAreas in the GUI. :returns: a dictionary containing the result of calling getTextArea for every TextArea in the GUI """ areas = {} for k in self.widgetManager.group(WIDGET_NAMES.TextArea): areas[k] = self.getTextArea(k) return areas def textAreaCreateTag(self, title, name, **kwargs): """ creates a new tag on the specified text area """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_config(name, **kwargs) def textAreaChangeTag(self, title, name, **kwargs): """ changes a tag on the specified text area """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_config(name, **kwargs) def textAreaDeleteTag(self, title, *tags): """ deletes the specified tag """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_delete(*tags) def textAreaTagPattern(self, title, tag, pattern, regexp=False): """ applies the tag to the specified text """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.highlightPattern(pattern, tag, regexp=regexp) def textAreaTagRange(self, title, tag, start, end=END): """ applies the tag to the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_add(tag, start, end) def textAreaTagSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaTagRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set() def textAreaUntagRange(self, title, tag, start, end=END): """removes the tag from the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_remove(tag, start, end) def textAreaToggleFontRange(self, title, tag, start, end=END): """ will toggle the tag at the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) tag = ta.verifyFontTag(tag) if tag in ta.tag_names(start): ta.tag_remove("AJ_"+tag, start, end) else: self.textAreaApplyFontRange(title, tag, start, end) def textAreaToggleFontSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaToggleFontRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set() def textAreaApplyFontSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaApplyFontRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set() def textAreaApplyFontRange(self, title, tag, start, end=END): """removes the tag from the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) tag = ta.verifyFontTag(tag) if tag != "UNDERLINE": ta.tag_remove("AJ_BOLD", start, end) ta.tag_remove("AJ_ITALIC", start, end) ta.tag_remove("AJ_BOLD_ITALIC", start, end) ta.tag_add("AJ_" + tag, start, end) def textAreaUntagSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaUntagRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set() def textAreaToggleTagRange(self, title, tag, start, end=END): """ will toggle the tag at the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) if tag in ta.tag_names(start): self.textAreaUntagRange(title, tag, start, end) else: self.textAreaTagRange(title, tag, start, end) def textAreaToggleTagSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaToggleTagRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set() def searchTextArea(self, title, pattern, start=None, stop=None, nocase=True, backwards=False): """ will find and highlight the specified text, returning the position """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) if start is None: start = ta.index(INSERT) pos = ta.search(pattern, start, stopindex=stop, nocase=nocase, backwards=backwards) ta.focus_set() if pos == "": return None else: end = str(pos) + " + " + str(len(pattern)) + " c" ta.see(pos) ta.tag_add(SEL, pos, end) ta.mark_set("insert", pos) return pos def getTextAreaTag(self, title, tag): """ returns all details about the specified tag """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) return ta.tag_config(tag) def getTextAreaTags(self, title): """ returns a list of all tags in the text area """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) return ta.tag_names() def setTextAreaFont(self, title, **kwargs): """ changes the font of a text area """ self.widgetManager.get(WIDGET_NAMES.TextArea, title).setFont(**kwargs) def setTextArea(self, title, text, end=True, callFunction=True, tag=None): """ Add the supplied text to the specified TextArea :param title: the TextArea to change :param text: the text to add to the TextArea :param end: where to insert the text, by default it is added to the end. Set end to False to add to the beginning. :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.pauseCallFunction(callFunction) # in case it's disabled _state = ta.cget('state') ta.config(state='normal') if end: pos = ta.index('end -1c linestart') ta.insert(END, text) ta.see(END) # if tag is not None: self.textAreaTagRange(title, tag, pos) else: ta.insert('1.0', text) ta.see('1.0') # if tag is not None: ta.textAreaTagPattern(title, tag, text) ta.config(state=_state) ta.resumeCallFunction() def clearTextArea(self, title, callFunction=True): """ Removes all text from the specified TextArea :param title: the TextArea to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.pauseCallFunction(callFunction) # in case it's disabled _state = ta.cget('state') ta.config(state='normal') ta.delete('1.0', END) ta.config(state=_state) ta.resumeCallFunction() def clearAllTextAreas(self, callFunction=False): """ Convenience function to clear all TextAreas in the GUI Will simply call clearTextArea on each TextArea :param callFunction: whether to generate an event to notify that the widget has changed :returns: None """ for ta in self.widgetManager.group(WIDGET_NAMES.TextArea): self.clearTextArea(ta, callFunction=callFunction) def highlightTextArea(self, title, start, end=END): """ selects text in the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_add(SEL, start, end) def logTextArea(self, title): """ Creates an md5 hash - can be used later to check if the TextArea has changed The hash is stored in the widget :param title: the TextArea to hash :returns: None :raises ItemLookupError: if the title can't be found """ self._loadHashlib() if hashlib is False: self.warn("Unable to log TextArea, hashlib library not available") else: text = self.widgetManager.get(WIDGET_NAMES.TextArea, title) text.__hash = text.getTextAreaHash() def textAreaChanged(self, title): """ Creates a temporary md5 hash - and compares it with a previously generated & stored hash The previous hash has to be generated manually, by calling logTextArea :param title: the TextArea to hash :returns: bool - True if the TextArea has changed or False if it hasn't :raises ItemLookupError: if the title can't be found """ self._loadHashlib() if hashlib is False: self.warn("Unable to log TextArea, hashlib library not available") else: text = self.widgetManager.get(WIDGET_NAMES.TextArea, title) return text.__hash != text.getTextAreaHash() ##################################### # FUNCTIONS to add Tree Widgets ##################################### def tree(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets trees all in one go """ widgKind = WIDGET_NAMES.Tree click = kwargs.pop("click", None) dblClick = kwargs.pop("dbl", None) edit = kwargs.pop("edit", None) editable = kwargs.pop("editable", None) showAttr = kwargs.pop("attributes", None) showMenu = kwargs.pop("menu", None) fg = kwargs.pop("fg", None) bg = kwargs.pop("bg", None) fgH = kwargs.pop("fgH", None) bgH = kwargs.pop("bgH", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists tree = self.getTree(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) tree = self.addTree(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) self.setTreeColours(title, fg, bg, fgH, bgH) if click is not None: self.setTreeClickFunction(title, click) if edit is not None: self.setTreeEditFunction(title, edit) if dblClick is not None: self.setTreeDoubleClickFunction(title, dblClick) if editable is not None: self.setTreeEditable(title, editable) if showAttr is not None: self.showTreeAttributes(title, showAttr) if showMenu is not None: self.showTreeMenu(title, showMenu) return tree def addTree(self, title, data, row=None, column=0, colspan=0, rowspan=0): ''' adds a navigatable tree, displaying the specified xml text ''' self.widgetManager.verify(WIDGET_NAMES.Tree, title) self._importAjtree() if parseString is False: self.warn("Unable to parse xml files. .addTree() not available") return if isinstance(data, UNIVERSAL_STRING): data = parseString(data) else: pass # assume xml object return self._buildTree(title, data, row, column, colspan, rowspan) def _buildTree(self, title, xmlDoc, row=None, column=0, colspan=0, rowspan=0): self.widgetManager.verify(WIDGET_NAMES.Tree, title) frame = ScrollPane( self.getContainer(), relief=RAISED, borderwidth=2, bg="#FFFFFF", highlightthickness=0, takefocus=1) self._positionWidget(frame, row, column, colspan, rowspan, "NSEW") treeData = self._makeAjTreeData()(xmlDoc) gui.trace("TreeData populated: %s", title) treeNode = self._makeAjTreeNode()(frame.getPane(), None, treeData) gui.trace("TreeNode created: %s", title) self.widgetManager.add(WIDGET_NAMES.Tree, title, treeNode) # update() & expand() called in go() function return treeNode # not complete yet... def clearTree(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.destroy() tree.update() def showTreeAttributes(self, title, show=True): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) self._loadTooltip() tree.showAttributes(show) # not complete yet... def showTreeMenu(self, title, show=True): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.showMenu(show) # not complete yet... def addTreeChild(self, title, data): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) if isinstance(data, UNIVERSAL_STRING): data = parseString(data) treeData = self._makeAjTreeData()(data) tree.addChild(treeData) def setTreeEditable(self, title, value=True): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.item.setCanEdit(value) def setTreeBg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setBgColour(colour) def setTreeFg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setFgColour(colour) def setTreeHighlightBg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setBgHColour(colour) def setTreeHighlightFg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setFgHColour(colour) def setTreeColours(self, title, fg=None, bg=None, fgH=None, bgH=None): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setAllColours(bg, fg, bgH, fgH) def setTreeDoubleClickFunction(self, title, func): if func is not None: tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.item.registerDblClick(title, func) def setTreeClickFunction(self, title, func): if func is not None: tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.item.registerClick(title, func) def setTreeEditFunction(self, title, func): if func is not None: tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) command = self.MAKE_FUNC(func, title) tree.registerEditEvent(command) # get whole tree as XML def getTreeXML(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) return tree.item.node.toxml() # get selected node as a string def getTreeSelected(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) return tree.getSelectedText() # get selected node (and children) as XML def getTreeSelectedXML(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) item = tree.getSelected() if item is not None: return item.node.toxml() else: return None def generateTree(self, title): """ displays data inside tree """ tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) gui.trace("Generating Tree: %s", title) tree.update() gui.trace("Tree updated: %s", title) tree.expand() gui.trace("Tree expanded: %s", title) ##################################### # FUNCTIONS to add Message Box ##################################### def message(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets messages all in one go """ widgKind = WIDGET_NAMES.Message try: self.widgetManager.verify(WIDGET_NAMES.Message, title) except: # widget exists if value is not None: self.setMessage(title, value) msg = self.getMessage(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) msg = self._messageMaker(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return msg def _messageMaker(self, title, text, row=None, column=0, colspan=0, rowspan=0, *args, **kwargs): return self.addMessage(title, text, row, column, colspan, rowspan) def addMessage(self, title, text=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a message box, to display text across multiple lines ''' self.widgetManager.verify(WIDGET_NAMES.Message, title) if text is None: text = title gui.trace("Not specifying text for messages (%s) now uses the title for the text. If you want an empty message, pass an empty string ''", title) mess = Message(self.getContainer()) mess.config(text=text) mess.config(font=self._getContainerProperty('labelFont')) mess.config(justify=LEFT, background=self._getContainerBg()) mess.DEFAULT_TEXT = text if self.platform in [self.MAC, self.LINUX]: mess.config(highlightbackground=self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.Message, title, mess) self._positionWidget(mess, row, column, colspan, rowspan) # mess.bind("<Configure>", lambda e: mess.config(width=e.width-10)) return mess def addEmptyMessage(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an empty message box ''' return self.addMessage(title, "", row, column, colspan, rowspan) def setMessage(self, title, text): mess = self.widgetManager.get(WIDGET_NAMES.Message, title) mess.config(text=text) def setMessageAspect(self, title, aspect): """ set a new aspect ratio for the text in this widget """ mess = self.widgetManager.get(WIDGET_NAMES.Message, title) mess.config(aspect=aspect) def clearMessage(self, title): self.setMessage(title, "") def getMessage(self, title): mess = self.widgetManager.get(WIDGET_NAMES.Message, title) return mess.cget("text") ##################################### # FUNCTIONS for entry boxes ##################################### def entry(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets entries all in one go """ widgKind = WIDGET_NAMES.Entry default = kwargs.pop("default", None) limit = kwargs.pop("limit", None) case = kwargs.pop("case", None) rows = kwargs.pop("rows", None) secret = kwargs.pop("secret", False) kind = kwargs.pop("kind", "standard").lower().strip() labBg = kwargs.pop("labBg", None) try: self.widgetManager.verify(WIDGET_NAMES.Entry, title) except: # widget exists if value is not None: self.setEntry(title, value, *args, **kwargs) ent = self.getEntry(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) # create the entry widget if kind == "auto": if value is None: value = [] ent = self._entryMaker(title, *args, secret=secret, kind=kind, words=value, **kwargs) else: ent = self._entryMaker(title, *args, secret=secret, kind=kind, **kwargs) if not ent: return # apply any setter values if limit is not None: self.setEntryMaxLength(title, limit) if case == "upper": self.setEntryUpperCase(title) elif case == "lower": self.setEntryLowerCase(title) if default is not None: self.setEntryDefault(title, default) if kind != "auto": if value is not None: self.setEntry(title, value) else: if rows is not None: self.setAutoEntryNumRows(title, rows) if labBg is not None and self.widgetManager.get(WIDGET_NAMES.Entry, title).isValidation: self.setValidationEntryLabelBg(title, labBg) # used by file entries kwargs.pop("text", None) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return ent def setValidationEntryLabelBg(self, title, bg): ent = self.widgetManager.get(WIDGET_NAMES.Entry, title) if not ent.isValidation: raise Exception("You can only set label BGs on validation entries") ent.lab.config(bg=bg) def _entryMaker(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=False, kind="standard", words=None, **kwargs): # used by file entries text = kwargs.pop("text", None) default = kwargs.pop("default", None) if not label: frame = self.getContainer() else: frame = self._getLabelBox(title, label=label, **kwargs) if kind == "standard": ent = self._buildEntry(title, frame, secret) elif kind == "numeric": ent = self._buildEntry(title, frame, secret) if self.validateNumeric is None: self.validateNumeric = (self.containerStack[0]['container'].register( self._validateNumericEntry), '%d', '%i', '%P', '%s', '%S', '%v', '%V', '%W') ent.isNumeric = True ent.config(validate='key', validatecommand=self.validateNumeric) self.setEntryTooltip(title, "Numeric data only.") elif kind == "auto": ent = self._buildEntry(title, frame, secret=False, words=words) elif kind in ["file", "open", "save", "directory"]: ent = self._buildFileEntry(title, frame, kind=kind, text=text, default=default) elif kind == "validation": ent = self._buildValidationEntry(title, frame, secret) else: raise Exception("Invalid entry kind: %s", kind) if not label: self._positionWidget(ent, row, column, colspan, rowspan) else: self._packLabelBox(frame, ent) self._positionWidget(frame, row, column, colspan, rowspan) return ent def addEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False): ''' adds an entry box for capturing text ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="standard") def addLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): ''' adds an entry box for capturing text, with the title as a label ''' return self._entryMaker(title, row, column, colspan, rowspan, secret, label=label) def addSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box for capturing text, where the text is displayed as stars ''' return self._entryMaker(title, row, column, colspan, rowspan, True) def addLabelSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label) def addSecretLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label) def addFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box with a button, that pops-up a file dialog ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="file") def addLabelFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box with a button, that pops-up a file dialog, with a label that displays the title ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="file") def addOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box with a button, that pops-up a open dialog ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="open") def addLabelOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box with a button, that pops-up a open dialog, with a label that displays the title ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="open") def addSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box with a button, that pops-up a save dialog ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="save") def addLabelSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box with a button, that pops-up a save dialog, with a label that displays the title ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="save") def addDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="directory") def addLabelDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="directory") def addValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="validation") def addLabelValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="validation") def addAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="auto", words=words) def addLabelAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="auto", words=words) def addNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False): return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="numeric") def addLabelNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric") def addNumericLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric") def _getDirName(self, title): self._getFileName(title, kind='directory') def _getSaveName(self, title): self._getFileName(title, kind='save') def _getFileName(self, title, kind='open'): if kind in ['open', 'file']: fileName = self.openBox() elif kind == 'save': fileName = self.saveBox() elif kind == 'directory': fileName = self.directoryBox() if fileName is not None and fileName != "": self.setEntry(title, fileName) self.topLevel.after(250, self.setEntryFocus, title) def _checkDirName(self, title): if len(self.getEntry(title)) == 0: self._getFileName(title, kind='directory') def _checkSaveName(self, title): if len(self.getEntry(title)) == 0: self._getFileName(title, kind='save') def _checkFileName(self, title): if len(self.getEntry(title)) == 0: self._getFileName(title, kind='open') def _buildEntry(self, title, frame, secret=False, words=[]): self.widgetManager.verify(WIDGET_NAMES.Entry, title) # if we are an autocompleter if len(words) > 0: ent = self._makeAutoCompleteEntry()(words, self._getTopLevel(), frame) else: var = StringVar(self.topLevel) ent = entryBase(frame, textvariable=var) ent.var = var ent.var.auto_id = None # for now - suppress UP/DOWN arrows if self.platform in [self.MAC]: def suppress(event): if event.keysym == "Up": # move home event.widget.icursor(0) event.widget.xview(0) return "break" elif event.keysym == "Down": # move end if not self.ttkFlag: event.widget.icursor(END) event.widget.xview(END) else: event.widget.icursor(END) event.widget.xview(len(event.widget.get())) return "break" ent.bind("<Key>", suppress) if not self.ttkFlag: ent.config(font=self._getContainerProperty('inputFont')) if self.platform in [self.MAC, self.LINUX]: ent.config(highlightbackground=self._getContainerBg()) # vars to store any limit traces ent.var.uc_id = None ent.var.lc_id = None ent.var.ml_id = None ent.inContainer = False ent.showingDefault = False # current status of entry ent.default = "" # the default value to show (if set) ent.DEFAULT_TEXT = "" # the default value for language support ent.myTitle = title # the title of the entry ent.isNumeric = False # if the entry is numeric ent.isValidation = False # if the entry is validation ent.isSecret = False # if the entry is secret # configure it to be secret if secret: ent.config(show="*") ent.isSecret = True ent.bind("<Tab>", self._focusNextWindow) ent.bind("<Shift-Tab>", self._focusLastWindow) # add a right click menu self._addRightClickMenu(ent) self.widgetManager.add(WIDGET_NAMES.Entry, title, ent) self.widgetManager.add(WIDGET_NAMES.Entry, title, ent.var, group=WidgetManager.VARS) return ent def _buildFileEntry(self, title, frame, kind='save', text=None, default=None): vFrame = self._makeButtonBox()(frame) self.widgetManager.log(WIDGET_NAMES.FrameBox, vFrame) if not self.ttkFlag: vFrame.config(background=self._getContainerBg()) vFrame.theWidget = self._buildEntry(title, vFrame) vFrame.theWidget.inContainer = True vFrame.theWidget.pack(expand=True, fill=X, side=LEFT) if kind in ['open', "file"]: command = self.MAKE_FUNC(self._getFileName, title) vFrame.theWidget.click_command = self.MAKE_FUNC(self._checkFileName, title) if text is None: text = "File" if default is None: default = "-- enter a filename --" elif kind == 'save': command = self.MAKE_FUNC(self._getSaveName, title) vFrame.theWidget.click_command = self.MAKE_FUNC(self._checkSaveName, title) if text is None: text = "File" if default is None: default = "-- enter a filename --" else: command = self.MAKE_FUNC(self._getDirName, title) vFrame.theWidget.click_command = self.MAKE_FUNC(self._checkDirName, title) if text is None: text = "Directory" if default is None: default = "-- enter a directory --" self.setEntryDefault(title, default) vFrame.theWidget.bind("<Button-1>", vFrame.theWidget.click_command, "+") if not self.ttkFlag: vFrame.theButton = Button(vFrame, font=self._getContainerProperty('buttonFont')) else: vFrame.theButton = ttk.Button(vFrame) vFrame.theButton.config(text=text) vFrame.theButton.config(command=command) vFrame.theButton.pack(side=RIGHT, fill=X) vFrame.theButton.inContainer = True vFrame.theButton.SKIP_CLEANSE = True vFrame.theWidget.but = vFrame.theButton if not self.ttkFlag and self.platform in [self.MAC, self.LINUX]: vFrame.theButton.config(highlightbackground=self._getContainerBg()) return vFrame def _buildValidationEntry(self, title, frame, secret): vFrame = self._makeLabelBox()(frame) self.widgetManager.log(WIDGET_NAMES.FrameBox, vFrame) vFrame.isValidation = True ent = self._buildEntry(title, vFrame, secret) if not self.ttkFlag: vFrame.config(background=self._getContainerBg()) ent.config(highlightthickness=2) ent.pack(expand=True, fill=X, side=LEFT) ent.isValidation = True ent.inContainer = True class ValidationLabel(labelBase, object): def __init__(self, parent, *args, **options): super(ValidationLabel, self).__init__(parent, *args, **options) lab = ValidationLabel(vFrame) lab.pack(side=RIGHT, fill=Y) lab.config(font=self._getContainerProperty('labelFont')) if not self.ttkFlag: lab.config(background=self._getContainerBg()) lab.inContainer = True lab.isValidation = True ent.lab = lab vFrame.theWidget = ent vFrame.theLabel = lab self.setEntryWaitingValidation(title) return vFrame def setEntryValid(self, title): self.setValidationEntry(title, "valid") def setEntryInvalid(self, title): self.setValidationEntry(title, "invalid") def setEntryWaitingValidation(self, title): self.setValidationEntry(title, "wait") def setValidationEntry(self, title, state="valid"): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) if not entry.isValidation: self.warn("Entry %s is not a validation entry. Unable to set WAITING VALID.", title) return if state == "wait": col = "#000000" text = '\u2731' eStyle="ValidationEntryWaiting.TEntry" lStyle="ValidationEntryWaiting.TLabel" elif state == "invalid": col = "#FF0000" text = '\u2716' eStyle="ValidationEntryInvalid.TEntry" lStyle="ValidationEntryInvalid.TLabel" elif state == "valid": col = "#4CC417" text = '\u2714' eStyle="ValidationEntryValid.TEntry" lStyle="ValidationEntryValid.TLabel" else: self.warn("Invalid validation state: %s", state) return if not self.ttkFlag: if not entry.showingDefault: entry.config(fg=col) entry.config(highlightbackground=col, highlightcolor=col) entry.config(highlightthickness=1) entry.lab.config(text=text, fg=col) entry.oldFg = col else: if not entry.showingDefault: entry.configure(style=eStyle) entry.lab.config(text=text, style=lStyle) entry.oldFg = eStyle entry.lab.DEFAULT_TEXT = entry.lab.cget("text") def appendAutoEntry(self, title, value): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.addWords(value) except AttributeError: gui.error("You can only append items to an AutoEntry, %s is not an AutoEntry.", title) def removeAutoEntry(self, title, value): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.removeWord(value) except AttributeError: gui.error("You can only remove items from an AutoEntry, %s is not an AutoEntry.", title) def changeAutoEntry(self, title, value): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.changeWords(value) except AttributeError: gui.error("You can only change items in an AutoEntry, %s is not an AutoEntry.", title) def setAutoEntryNumRows(self, title, rows): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.setNumRows(rows) except AttributeError: gui.error("You can only change the number of rows in an AutoEntry, %s is not an AutoEntry.", title) def _validateNumericEntry(self, action, index, value_if_allowed, prior_value, text, validation_type, trigger_type, widget_name): if action == "1": if str(text) in '0123456789.-+': try: if len(str(value_if_allowed)) == 1 and str(value_if_allowed) in '.-': return True elif len(str(value_if_allowed)) == 2 and str(value_if_allowed) == '-.': return True else: float(value_if_allowed) return True except ValueError: self.containerStack[0]['container'].bell() return False else: self.containerStack[0]['container'].bell() return False else: return True def getEntry(self, name): entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) if entry.showingDefault: if entry.isNumeric: return None else: return "" else: val = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS).get() if entry.isNumeric: if len(val) == 0 or (len(val) == 1 and val in '.-') or (len(val) == 2 and val == "-."): return None else: return float(val) else: return val def getAllEntries(self): entries = {} for k in self.widgetManager.group(WIDGET_NAMES.Entry): entries[k] = self.getEntry(k) return entries def setEntry(self, name, text, callFunction=True): ent = self.widgetManager.get(WIDGET_NAMES.Entry, name) var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) self._updateEntryDefault(name, mode="set") # now call function with PauseCallFunction(callFunction, var, False): if not ent.isNumeric or self._validateNumericEntry("1", None, text, None, "1", None, None, None): var.set(text) def setEntryMaxLength(self, name, length): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) var.maxLength = length if var.ml_id is not None: var.trace_vdelete('w', var.ml_id) var.ml_id = var.trace('w', self.MAKE_FUNC(self._limitEntry, name)) def setEntryUpperCase(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) if var.uc_id is not None: var.trace_vdelete('w', var.uc_id) var.uc_id = var.trace('w', self.MAKE_FUNC(self._upperEntry, name)) def setEntryLowerCase(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) if var.lc_id is not None: var.trace_vdelete('w', var.lc_id) var.lc_id = var.trace('w', self.MAKE_FUNC(self._lowerEntry, name)) def _limitEntry(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) if len(var.get()) > var.maxLength: self.containerStack[0]['container'].bell() var.set(var.get()[0:var.maxLength]) def _upperEntry(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) chars = var.get().upper() var.set(chars) def _lowerEntry(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) chars = var.get().lower() var.set(chars) def _entryIn(self, name): self._updateEntryDefault(name, "in") def _entryOut(self, name): self._updateEntryDefault(name, "out") def _updateEntryDefault(self, name, mode=None): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) # ignore this if no default to apply if entry.default == "": return # disable any limits if var.lc_id is not None: var.trace_vdelete('w', var.lc_id) if var.uc_id is not None: var.trace_vdelete('w', var.uc_id) if var.ml_id is not None: var.trace_vdelete('w', var.ml_id) # disable any auto completion if var.auto_id is not None: var.trace_vdelete('w', var.auto_id) current = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS).get() # disable any change function with PauseCallFunction(False, var, False): # clear & remove default if mode == "set" or (mode in [ "in", "clear"] and entry.showingDefault): var.set("") entry.showingDefault = False entry.config(justify=entry.oldJustify) if not self.ttkFlag: entry.config(foreground=entry.oldFg) else: entry.configure(style=entry.oldFg) if entry.isSecret: entry.config(show="*") elif mode == "out" and (current == "" or entry.showingDefault): if entry.isSecret: entry.config(show="") var.set(entry.default) entry.config(justify='center') if not self.ttkFlag: entry.config(foreground='grey') else: entry.configure(style="DefaultText.TEntry") entry.showingDefault = True elif mode == "update" and entry.showingDefault: if entry.isSecret: entry.config(show="") var.set(entry.default) # re-enable any limits if var.lc_id is not None: var.lc_id = var.trace('w', self.MAKE_FUNC(self._lowerEntry, name)) if var.uc_id is not None: var.uc_id = var.trace('w', self.MAKE_FUNC(self._upperEntry, name)) if var.ml_id is not None: var.ml_id = var.trace('w', self.MAKE_FUNC(self._limitEntry, name)) # re-enable auto completion if var.auto_id is not None: var.auto_id = var.trace('w', entry.textChanged) def setEntryDefault(self, name, text="default"): entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) # remember current settings - to return to if not hasattr(entry, "oldJustify"): entry.oldJustify = entry.cget('justify') if not hasattr(entry, "oldFg"): if not self.ttkFlag: entry.oldFg = entry.cget('foreground') else: entry.oldFg = entry.cget("style") # configure default stuff entry.default = text entry.DEFAULT_TEXT = text # only show new text if empty self._updateEntryDefault(name, "out") # bind commands to show/remove the default if hasattr(entry, "defaultInEvent"): entry.unbind(entry.defaultInEvent) entry.unbind(entry.defaultOutEvent) in_command = self.MAKE_FUNC(self._entryIn, name) out_command = self.MAKE_FUNC(self._entryOut, name) entry.defaultInEvent = entry.bind("<FocusIn>", in_command, add="+") entry.defaultOutEvent = entry.bind("<FocusOut>", out_command, add="+") def clearEntry(self, name, callFunction=True, setFocus=True): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) # now call function with PauseCallFunction(callFunction, var, False): var.set("") self._updateEntryDefault(name, mode="clear") if setFocus: self.setFocus(name) def clearAllEntries(self, callFunction=False): for entry in self.widgetManager.group(WIDGET_NAMES.Entry, group=WidgetManager.VARS): self.clearEntry(entry, callFunction=callFunction, setFocus=False) def setFocus(self, name): entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) entry.focus_set() def getFocus(self): widg = self.topLevel.focus_get() return self.widgetManager.getName(widg) #################################### ## Functions to get widget details #################################### def _lookupValue(self, myDict, val): for name in myDict: if isinstance(myDict[name], type([])): # array of cbs for rb in myDict[name]: if rb == val: return name else: if myDict[name] == val: return name return None ##################################### # FUNCTIONS for progress bars (meters) ##################################### def meter(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets meters all in one go """ widgKind = WIDGET_NAMES.Meter kind = kwargs.pop("kind","'meter") fill = kwargs.pop("fill", None) text = kwargs.pop("text", None) try: self.widgetManager.verify(WIDGET_NAMES.Meter, title) except: # widget exists meter = self.getMeter(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "split": meter = self._addMeter(title, "SPLIT", **kwargs) elif kind == "dual": meter = self._addMeter(title, "DUAL", **kwargs) else: meter = self._addMeter(title, "METER", **kwargs) if value is not None: self.setMeter(title, value, text=text) if fill is not None: self.setMeterFill(title, fill) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return meter def _addMeter(self, name, kind="METER", row=None, column=0, colspan=0, rowspan=0, **kwargs): self.widgetManager.verify(WIDGET_NAMES.Meter, name) if kind == "SPLIT": meter = SplitMeter(self.getContainer(), font=self._getContainerProperty('labelFont')) elif kind == "DUAL": meter = DualMeter(self.getContainer(), font=self._getContainerProperty('labelFont')) else: meter = Meter(self.getContainer(), font=self._getContainerProperty('labelFont')) self.widgetManager.add(WIDGET_NAMES.Meter, name, meter) self._positionWidget(meter, row, column, colspan, rowspan) return meter def addMeter(self, name, row=None, column=0, colspan=0, rowspan=0): return self._addMeter(name, "METER", row, column, colspan, rowspan) def addSplitMeter(self, name, row=None, column=0, colspan=0, rowspan=0): return self._addMeter(name, "SPLIT", row, column, colspan, rowspan) def addDualMeter(self, name, row=None, column=0, colspan=0, rowspan=0): return self._addMeter(name, "DUAL", row, column, colspan, rowspan) # update the value of the specified meter # note: expects a value between 0 (-100 for split/dual) & 100 def setMeter(self, name, value=0.0, text=None): item = self.widgetManager.get(WIDGET_NAMES.Meter, name) item.set(value, text) def getMeter(self, name): item = self.widgetManager.get(WIDGET_NAMES.Meter, name) return item.get() def getAllMeters(self): meters = {} for k in self.widgetManager.group(WIDGET_NAMES.Meter): meters[k] = self.getMeter(k) return meters # a single colour for meters, a list of 2 colours for splits & duals def setMeterFill(self, name, colour): item = self.widgetManager.get(WIDGET_NAMES.Meter, name) item.configure(fill=colour) ##################################### # FUNCTIONS for seperators ##################################### def separator(self, *args, **kwargs): """ simpleGUI - adds horizontal/vertical separators """ direction = kwargs.pop("direction", "horizontal").lower() kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if direction == "vertical": return self.addVerticalSeparator(*args, **kwargs) else: return self.addHorizontalSeparator(*args, **kwargs) def addHorizontalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None): return self._addSeparator("horizontal", row, column, colspan, rowspan, colour) def addVerticalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None): return self._addSeparator("vertical", row, column, colspan, rowspan, colour) def _addSeparator(self, orient, row=None, column=0, colspan=0, rowspan=0, colour=None): sep = self._makeSeparator()(self.getContainer(), orient) if colour is not None: sep.configure(fg=colour) self.widgetManager.log(WIDGET_NAMES.Separator, sep) self._positionWidget(sep, row, column, colspan, rowspan) return sep ##################################### # FUNCTIONS for pie charts ##################################### def pie(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets pies all in one go """ widgKind = WIDGET_NAMES.PieChart name = kwargs.pop("name", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists if name is not None: self.setPieChart(title, name, value) pie = self.getPieChart(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) pie = self.addPieChart(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return pie def addPieChart(self, name, fracs, row=None, column=0, colspan=0, rowspan=0): self.widgetManager.verify(WIDGET_NAMES.PieChart, name) self._loadTooltip() pie = PieChart(self.getContainer(), fracs, self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.PieChart, name, pie) self._positionWidget(pie, row, column, colspan, rowspan, sticky=None) return pie def setPieChart(self, title, name, value): pie = self.widgetManager.get(WIDGET_NAMES.PieChart, title) pie.setValue(name, value) ##################################### # FUNCTIONS for toolbar ##################################### # adds a list of buttons along the top - like a tool bar... def addToolbarButton(self, name, func, findIcon=False): self.addToolbar([name], func, findIcon) def toolbar(self, names, funcs, **kwargs): """ simpleGUI - shortener for toolbar """ icons = kwargs.pop('icons', kwargs.pop('findIcon', False)) pinned = kwargs.pop('pinned', None) disabled = kwargs.pop('disabled', None) hidden = kwargs.pop('hidden', None) status = kwargs.pop('status', None) bg = kwargs.pop('bg', None) if bg is not None: self.setToolbarBg(bg) self.addToolbar(names, funcs, findIcon=icons is not False) # allow status and icon name to be passed in a list for x, n in enumerate(names): if icons is not None: try: self.setToolbarIcon(n, icons[x]) except: pass if status is not None: try: self.setToolbarButtonDisabled(n, not status[x]) except: pass if pinned is not None: self.setToolbarPinned(pinned=pinned) if disabled is not None: self.setToolbarDisabled(disabled=disabled) if hidden is True: self.hideToolbar() def addToolbar(self, names, funcs, findIcon=False, **kwargs): # hide the toolbarMin bar if self.tb.toolbarMin is not None: self.tb.toolbarMin.pack_forget() # make sure the toolbar is showing try: self.tb.pack_info() except: self.tb.location = self.containerStack[0]['container'] self.tb.pack(before=self.tb.location, side=TOP, fill=X) if not self.tb.inUse: self.tb.inUse = True image = None singleFunc = self._checkFunc(names, funcs) if not isinstance(names, list): names = [names] for i in range(len(names)): t = names[i] if (t in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception( "Invalid toolbar button name: " + t + " already exists") if findIcon: # turn off warnings about PNGs with PauseLogger(): imgFile = os.path.join(self.icon_path, t.lower() + ".png") try: image = self._getImage(imgFile) except Exception as e: image = None if not self.ttkFlag: but = Button(self.tb) but.config(relief=FLAT, font=self._buttonFont) if gui.GET_PLATFORM() == gui.MAC and self.tb.BG_COLOR is not None: but.config(highlightbackground=self.tb.BG_COLOR) else: but = ttk.Button(self.tb) self.widgetManager.add(WIDGET_NAMES.Toolbar, t, but) if singleFunc is not None: u = self.MAKE_FUNC(singleFunc, t) else: u = self.MAKE_FUNC(funcs[i], t) but.config(command=u) if image is not None: # works on Mac & Windows :) but.config(image=image) but.image = image if not self.ttkFlag: but.config(justify=LEFT, compound=TOP) else: but.config(style="Toolbar.TButton") else: but.config(text=t) but.pack(side=LEFT, padx=2, pady=2) but.tt_var = self._addTooltip(but, t.title(), True) but.DEFAULT_TEXT=t def _setPinBut(self): # only call this once if self.tb.pinBut is not None: return # try to get the icon, if none - then set but to None, and ignore from now on imgFile = os.path.join(self.icon_path, "pin.gif") try: imgObj = self._getImage(imgFile) if not self.ttkFlag: self.tb.pinBut = Label(self.tb) if self.tb.BG_COLOR is not None: self.tb.pinBut.config(bg=self.tb.BG_COLOR) else: self.tb.pinBut = ttk.Label(self.tb) self.tb.pinBut.config(style="Toolbar.TLabel") except: return # if image found, then set up the label if self.tb.pinBut is not None: self.tb.pinBut.config(image=imgObj)#, compound=TOP, text="", justify=LEFT) self.tb.pinBut.image = imgObj # keep a reference! self.tb.pinBut.pack(side=RIGHT, anchor=NE, padx=0, pady=0) if gui.GET_PLATFORM() == gui.MAC: self.tb.pinBut.config(cursor="pointinghand") elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.tb.pinBut.config(cursor="hand2") self.tb.pinBut.eventId = self.tb.pinBut.bind("<Button-1>", self._toggletb) self._addTooltip(self.tb.pinBut, "Click here to pin/unpin the toolbar.", True) # called by pinBut, to toggle the pin status of the toolbar def _toggletb(self, event=None): self.setToolbarPinned(not self.tb.pinned) def setToolbarPinned(self, pinned=True): self.tb.pinned = pinned self._setPinBut() if not self.tb.pinned: if self.tb.pinBut is not None: try: self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "unpin.gif")) except: pass self.tb.makeMinBar() self.tb._minToolbar() else: if self.tb.pinBut is not None: try: self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "pin.gif")) except: pass self.tb._maxToolbar() if self.tb.pinBut is not None: self.tb.pinBut.config(image=self.tb.pinBut.image) def setToolbarIcon(self, name, icon): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) imgFile = os.path.join(self.icon_path, icon.lower() + ".png") with PauseLogger(): self.setToolbarImage(name, imgFile) # self.widgetManager.get(WIDGET_NAMES.Toolbar, name).tt_var.set(icon) def setToolbarBg(self, bg): self.tb.BG_COLOR = bg if not self.ttkFlag: self.tb.config(bg=self.tb.BG_COLOR) if gui.GET_PLATFORM() == gui.MAC: for name, val in self.widgetManager.group(WIDGET_NAMES.Toolbar).items(): val.config(highlightbackground=self.tb.BG_COLOR) # config the pin button if exists if self.tb.pinBut is not None: self.tb.pinBut.config(bg=self.tb.BG_COLOR) else: self.ttkStyle.configure("Toolbar.TFrame", background=self.tb.BG_COLOR) self.ttkStyle.configure("Toolbar.TLabel", background=self.tb.BG_COLOR) def setToolbarImage(self, name, imgFile): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) image = self._getImage(imgFile) self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(image=image) self.widgetManager.get(WIDGET_NAMES.Toolbar, name).image = image def removeToolbarButton(self, name, hide=True): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) self.widgetManager.get(WIDGET_NAMES.Toolbar, name).destroy() self.widgetManager.remove(WIDGET_NAMES.Toolbar, name) if hide: if len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) == 0: self.tb.pack_forget() self.tb.inUse = False if self.tb.toolbarMin is not None: self.tb.toolbarMin.pack_forget() def removeToolbar(self, hide=True): while len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) > 0: self.removeToolbarButton(list(self.widgetManager.group(WIDGET_NAMES.Toolbar))[0], hide) def setToolbarButtonEnabled(self, name): self.setToolbarButtonDisabled(name, False) def setToolbarButtonDisabled(self, name, disabled=True): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) if disabled: self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=DISABLED) else: self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=NORMAL) def setToolbarEnabled(self): self.setToolbarDisabled(False) def setToolbarDisabled(self, disabled=True): for but in self.widgetManager.group(WIDGET_NAMES.Toolbar).keys(): if disabled: self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=DISABLED) else: self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=NORMAL) if self.tb.pinBut is not None: if disabled: # this fails if not bound if self.tb.pinBut.eventId: self.tb.pinBut.unbind("<Button-1>", self.tb.pinBut.eventId) self.tb.pinBut.eventId = None self._disableTooltip(self.tb.pinBut) self.tb.pinBut.config(cursor="") else: if gui.GET_PLATFORM() == gui.MAC: self.tb.pinBut.config(cursor="pointinghand") elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.tb.pinBut.config(cursor="hand2") self.tb.pinBut.eventId = self.tb.pinBut.bind("<Button-1>", self._toggletb) self._enableTooltip(self.tb.pinBut) # functions to hide & show the toolbar def hideToolbar(self): self.tb.hide() def showToolbar(self): self.tb.show() # Method to get all inputs. def getAllInputs(self, **kwargs): """Get all values, merge & return as a single dictionary. :param kwargs: will be _appended_ to the input list. Note, empty pairs from each input is stripped, existing keys will not be overridden! """ # used to stop removal of empty inputs includeEmptyInputs = kwargs.pop('includeEmptyInputs', False) # All available inputs. inputs = filter(None, [ self.getAllEntries(), self.getAllOptionBoxes(), self.getAllSpinBoxes(), self.getAllListBoxes(), self.getAllProperties(), self.getAllCheckBoxes(), self.getAllRadioButtons(), self.getAllScales(), self.getAllMeters(), self.getAllDatePickers(), kwargs, ]) result = data = dict() for pairs in inputs: for key, val in pairs.items(): # Try and strip values. try: val = val.strip() except AttributeError: pass try: # Skip if value is empty or if key already exists. if (not includeEmptyInputs and not val) or result[key]: continue except KeyError: pass result[key] = val return result ##################################### # FUNCTIONS for menu bar ##################################### def _initMenu(self): # create a menu bar - only shows if populated if not self.hasMenu: # self.topLevel.option_add('*tearOff', FALSE) self.hasMenu = True self.menuBar = Menu(self.topLevel) if self.platform == self.MAC: appmenu = Menu(self.menuBar, name='apple') self.menuBar.add_cascade(menu=appmenu) self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_APP", appmenu) elif self.platform == self.WINDOWS: # sysMenu must be added last, otherwise other menus vanish sysMenu = Menu(self.menuBar, name='system', tearoff=False) self.widgetManager.add(WIDGET_NAMES.Menu, "WIN_SYS", sysMenu) # add a parent menu, for menu items def createMenu(self, title, tearable=False, showInBar=True): self.widgetManager.verify(WIDGET_NAMES.Menu, title) self._initMenu() if title == "WIN_SYS" and self.platform != self.WINDOWS: self.warn("The WIN_SYS menu is specific to Windows") return None if self.platform == self.MAC and tearable: self.warn("Tearable menus (%s) not supported on MAC", title) tearable = False theMenu = Menu(self.menuBar, tearoff=tearable) if showInBar: self.menuBar.add_cascade(label=title, menu=theMenu) self.widgetManager.add(WIDGET_NAMES.Menu, title, theMenu) return theMenu def createRightClickMenu(self, title, showInBar=False): men = self.createMenu(title, False, showInBar) men.bind("<FocusOut>", lambda e: men.unpost()) return men def _bindRightClick(self, item, value): if self.platform in [self.WINDOWS, self.LINUX]: item.bind('<Button-3>', lambda e, menu=value: self._rightClick(e, menu)) else: item.bind('<Button-2>', lambda e, menu=value: self._rightClick(e, menu)) # add items to the named menu def addMenuItem(self, title, item, func=None, kind=None, shortcut=None, underline=-1, rb_id=None, createBinding=True): # set the initial menubar self._initMenu() # get or create an initial menu if title is not None: try: theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) except: theMenu = self.createMenu(title) if theMenu is None: gui.warn('Unable to create menu: %s', title) return if underline > -1 and self.platform == self.MAC: gui.warn("Underlining menu items not available on MAC") if func is not None: func = self.MAKE_FUNC(func, item) acc = None if shortcut is not None: if kind == 'cb': f = lambda e: self._menuCheckButtonBind(title, item, func) binding = EventBinding(shortcut, f, self._getTopLevel(), menuBinding=True) else: binding = EventBinding(shortcut, func, self._getTopLevel(), menuBinding=True) try: self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding) if createBinding: binding.createBindings() acc = binding.displayName except ItemLookupError: raise ItemLookupError('Unable to bind menu ' + item + ' to ' + binding.displayName + ' - binding already exists') # now, let's create the actual menu item if item == "-" or kind == "separator": theMenu.add_separator() elif kind == "topLevel" or title is None: if self.platform == self.MAC: self.warn("Unable to make topLevel menus (%s) on Mac", item) else: self.menuBar.add_command( label=item, command=func, accelerator=acc, underline=underline) elif kind == "rb": varName = title + "rb" + item newRb = False if (varName in self.widgetManager.group(WIDGET_NAMES.Menu, group=WidgetManager.VARS)): var = self.widgetManager.get(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS) else: newRb = True var = StringVar(self.topLevel) self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS) theMenu.add_radiobutton(label=rb_id, command=func, variable=var, value=rb_id, accelerator=acc, underline=underline) if newRb: self.setMenuRadioButton(title, item, rb_id) elif kind == "cb": varName = title + "cb" + item self.widgetManager.verify(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS) var = BooleanVar(self.topLevel) var.set(False) self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS) theMenu.add_checkbutton(label=item, command=func, variable=var, onvalue=True, offvalue=False, accelerator=acc, underline=underline) elif kind == "sub": self.widgetManager.verify(WIDGET_NAMES.Menu, item) subMenu = Menu(theMenu, tearoff=False) self.widgetManager.add(WIDGET_NAMES.Menu, item, subMenu) theMenu.add_cascade(label=item, menu=subMenu) else: theMenu.add_command(label=item, command=func, accelerator=acc, underline=underline) # used to wrap check button bindings, so can also toggle def _menuCheckButtonBind(self, title, item, func): self.setMenuCheckBox(title, item) func(item) ################# # wrappers for other menu types def addMenuList(self, menuName, names, funcs): # deal with a dict_keys object - messy!!!! if not isinstance(names, list): names = list(names) # append some Nones, if it's a list and contains separators if funcs is not None: if not callable(funcs): seps = names.count("-") for i in range(seps): funcs.append(None) singleFunc = self._checkFunc(names, funcs) # add menu items for t in names: if funcs is None: u = None elif singleFunc is not None: u = singleFunc else: u = funcs.pop(0) self.addMenuItem(menuName, t, u) def _prepareCopyAndPasteMenu(self, event, widget=None): if self.copyAndPaste.inUse: if event is not None: widget = event.widget self._changeMenuState(self.widgetManager.get(WIDGET_NAMES.Menu, "EDIT"), DISABLED, 'Disabling', 10) self.copyAndPaste.setUp(widget) if self.copyAndPaste.canCopy: self.enableMenuItem("EDIT", "Copy") if self.copyAndPaste.canCut: self.enableMenuItem("EDIT", "Cut") if self.copyAndPaste.canPaste: self.enableMenuItem("EDIT", "Paste") self.enableMenuItem("EDIT", "Clear Clipboard") if self.copyAndPaste.canSelect: self.enableMenuItem("EDIT", "Select All") self.enableMenuItem("EDIT", "Clear All") if self.copyAndPaste.canUndo: self.enableMenuItem("EDIT", "Undo") if self.copyAndPaste.canRedo: self.enableMenuItem("EDIT", "Redo") if self.copyAndPaste.canFont: self.enableMenuItem("EDIT", "Bold") self.enableMenuItem("EDIT", "Italic") self.enableMenuItem("EDIT", "Bold & Italic") self.enableMenuItem("EDIT", "Underline") return True else: return False # called when copy/paste menu items are clicked def _copyAndPasteHelper(self, menu): if menu == "Cut": self.copyAndPaste.cut() elif menu == "Copy": self.copyAndPaste.copy() elif menu == "Paste": self.copyAndPaste.paste() elif menu == "Select All": self.copyAndPaste.selectAll() elif menu == "Clear Clipboard": self.copyAndPaste.clearClipboard() elif menu == "Clear All": self.copyAndPaste.clearText() elif menu == "Undo": self.copyAndPaste.undo() elif menu == "Redo": self.copyAndPaste.redo() elif menu in ["BOLD", "ITALIC", "UNDERLINE", "BOLD_ITALIC"]: self.copyAndPaste.font("AJ_"+menu) # add a single entry for a menu def addSubMenu(self, menu, subMenu): self.addMenuItem(menu, subMenu, func=None, kind="sub") def addMenu(self, name, func, shortcut=None, underline=-1): self.addMenuItem(None, name, func=func, kind="topLevel", shortcut=shortcut, underline=underline) def addMenuSeparator(self, menu): self.addMenuItem(menu, "-") def addMenuCheckBox(self, menu, name, func=None, shortcut=None, underline=-1): self.addMenuItem(menu, name, func, "cb", shortcut, underline) def addMenuRadioButton(self, menu, name, value, func=None, shortcut=None, underline=-1): self.addMenuItem(menu, name, func, "rb", shortcut, underline, value) def menu(self, menu, name=None, func=None, **kwargs): # kind: menu, sub, button, sep, check/tick, radio kind = kwargs.pop('kind', 'button') group = kwargs.pop('group', None) shortcut = kwargs.pop('shortcut', None) underline = kwargs.pop('underline', -1) tear = kwargs.pop('tear', False) state = kwargs.pop('state', None) image = kwargs.pop('image', None) icon = kwargs.pop('icon', None) align = kwargs.pop('align', 'left') if kind == 'menu': self.createMenu(menu, tearable=tear, showInBar=True) elif kind.startswith('sub'): self.addSubMenu(menu, name) elif kind.startswith('radio') or group is not None: self.addMenuRadioButton(menu, group, value=name, func=func, shortcut=shortcut, underline=underline) elif kind == 'button': if name is None and func is not None: self.addMenu(menu, func=func, shortcut=shortcut, underline=underline) elif name is None: self.createMenu(menu, tearable=tear, showInBar=True) elif isinstance(name, (list, tuple)): self.addMenuList(menu, name, func) else: self.addMenuItem(menu, name, func=func, kind=None, shortcut=shortcut, underline=underline) elif kind.startswith('sep'): self.addMenuSeparator(menu) elif kind.startswith('check') or kind.startswith('tick'): self.addMenuCheckBox(menu, name, func=func, shortcut=shortcut, underline=underline) if state is not None: if kind == 'menu' or kind.startswith('sub'): if state == 'disabled': self.disableMenu(menu, name) elif state == 'enabled': self.enableMenu(menu, name) else: if state == 'disabled': self.disableMenuItem(menu, name) elif state == 'enabled': self.enableMenuItem(menu, name) if image is not None: self.setMenuImage(menu, name, image, align=align) if icon is not None: self.setMenuIcon(menu, name, icon, align=align) ################# # wrappers for setters def _setMenu(self, menu, title, value, kind): title = menu + kind + title var = self.widgetManager.get(WIDGET_NAMES.Menu, title, group=WidgetManager.VARS) if kind == "rb": var.set(value) elif kind == "cb": if value is None: var.set(not var.get()) else: var.set(value) def setMenuCheckBox(self, menu, name, value=None): self._setMenu(menu, name, value, "cb") def setMenuRadioButton(self, menu, name, value): self._setMenu(menu, name, value, "rb") # set align = "none" to remove text def setMenuImage(self, menu, title, image, align="left"): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, menu) imageObj = self._getImage(image) if 16 != imageObj.width() or imageObj.width() != imageObj.height(): self.warn("Invalid image resolution for menu item %s (%s) - should be 16x16", title, image) #imageObj = imageObj.subsample(2,2) try: theMenu.entryconfigure(title, image=imageObj, compound=align) except TclError: gui.error("Unable to set image for menu item: %s, in menu: %s - item not found", title, menu) def setMenuIcon(self, menu, title, icon, align="left"): image = os.path.join(self.icon_path, icon.lower() + ".png") with PauseLogger(): self.setMenuImage(menu, title, image, align) def disableMenubar(self): gui.trace('Disabling toplevel menubar') self._disableMenu(self.menuBar) def enableMenubar(self): gui.trace('Enabling toplevel menubar') self._enableMenu(self.menuBar) def disableMenu(self, title): gui.trace('Disabling submenu: %s', title) theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) self._disableMenu(theMenu) def enableMenu(self, title): gui.trace('Enabling submenu: %s', title) theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) self._enableMenu(theMenu) def disableMenuItem(self, title, item): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: gui.trace("Disabling menu item: %s, in menu: %s", item, title) self._changeMenuItemState(theMenu, item, DISABLED) except TclError: gui.error("Unable to disable menu item: %s, in menu: %s - item not found", item, title) def enableMenuItem(self, title, item): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: gui.trace("Enabling menu item: %s, in menu: %s", item, title) self._changeMenuItemState(theMenu, item, NORMAL) except TclError: gui.error("Unable to enable menu item: %s, in menu: %s - item not found", item, title) def _disableMenu(self, menu): self._changeMenuState(menu, DISABLED, 'Disabling') def _enableMenu(self, menu): self._changeMenuState(menu, NORMAL, 'Enabling') def _changeMenuState(self, menu, state, text, limit=None): # changes the specified menu object's state to the new state, using the specified text for logging numMenus = menu.index("end") if numMenus is not None: # MAC_APP (and others?) returns None for item in range(numMenus+1): # we can cut-off early if requested internally if limit is not None and limit == item: break if menu.type(item) == 'cascade': label = menu.entrycget(item, 'label') if len(label) == 0: label = 'SPECIAL MENU' gui.trace('%s submenu: %s', text, label) subMenu = self.topLevel.nametowidget(menu.entrycget(item, 'menu')) self._changeMenuState(subMenu, state, text) menu.entryconfig(item, state=state) elif menu.type(item) in ['separator', 'tearoff']: gui.trace('Skipping separator/tearoff') else: label = menu.entrycget(item, 'label') gui.trace('%s item: %s', text, label) # use the position - if the label is a number it breaks... self._changeMenuItemState(menu, item, state) def _changeMenuItemState(self, menu, item, state): menu.entryconfigure(item, state=state) bindingName = menu.entrycget(item, 'accelerator') if bindingName is not None and len(bindingName) > 0: self.widgetManager.get(WIDGET_NAMES.Bindings, bindingName).changeBindings(state) def renameMenu(self, title, newName): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: self.menuBar.entryconfigure(title, label=newName) except TclError: gui.error("Unable to rename menu: %s - item not found", title) def renameMenuItem(self, title, item, newName): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: theMenu.entryconfigure(item, label=newName) except TclError: gui.error("Unable to rename menu item: %s, in menu: %s - item not found", item, title) def deleteMenuItem(self, title, item): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: bindingName = theMenu.entrycget(item, 'accelerator') theMenu.delete(item) self.widgetManager.get(WIDGET_NAMES.Bindings, bindingName).removeBindings() self.widgetManager.remove(WIDGET_NAMES.Bindings, bindingName) except TclError: gui.error("Unable to delete menu item: %s, in menu: %s - item not found", item, title) ################# # wrappers for getters def _getMenu(self, menu, title, kind): title = menu + kind + title var = self.widgetManager.get(WIDGET_NAMES.Menu, title, group=WidgetManager.VARS) if kind == 'rb': return var.get() else: if var.get(): return True else: return False def getMenuCheckBox(self, menu, title): return self._getMenu(menu, title, "cb") def getMenuRadioButton(self, menu, title): return self._getMenu(menu, title, "rb") ################# # wrappers for platform specific menus # enables the preferences item in the app menu def addMenuPreferences(self, func): if self.platform == self.MAC: self._initMenu() u = self.MAKE_FUNC(func, "preferences") self.topLevel.createcommand('tk::mac::ShowPreferences', u) else: self.warn("The Preferences Menu is specific to Mac OSX") # MAC help menu def addMenuHelp(self, func): if self.platform == self.MAC: self._initMenu() helpMenu = Menu(self.menuBar, name='help') self.menuBar.add_cascade(menu=helpMenu, label='Help') u = self.MAKE_FUNC(func, "help") self.topLevel.createcommand('tk::mac::ShowHelp', u) self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_HELP", helpMenu) else: self.warn("The Help Menu is specific to Mac OSX") # Shows a Window menu def addMenuWindow(self): if self.platform == self.MAC: self._initMenu() windowMenu = Menu(self.menuBar, name='window') self.menuBar.add_cascade(menu=windowMenu, label='Window') self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_WIN", windowMenu) else: self.warn("The Window Menu is specific to Mac OSX") def disableMenuEdit(self): self.copyAndPaste.inUse = False # adds an edit menu - by default only as a pop-up #Â if inMenuBar is True - then show in menu too def addMenuEdit(self, inMenuBar=False): self._initMenu() self.copyAndPaste.inUse = True # in case we already made the menu - just return try: self.widgetManager.verify(WIDGET_NAMES.Menu, "EDIT") except: return editMenu = Menu(self.menuBar, tearoff=False) editMenu.bind("<FocusOut>", lambda e: editMenu.unpost()) if inMenuBar: self.menuBar.add_cascade(menu=editMenu, label='Edit ') self.widgetManager.add(WIDGET_NAMES.Menu, "EDIT", editMenu) if gui.GET_PLATFORM() == gui.MAC: shortcut = "Command-" else: shortcut = "Control-" eList = [ ('Cut', lambda e: self._copyAndPasteHelper("Cut"), "X", False), ('Copy', lambda e: self._copyAndPasteHelper("Copy"), "C", False), ('Paste', lambda e: self._copyAndPasteHelper("Paste"), "V", False), ('Select All', lambda e: self._copyAndPasteHelper("Select All"), "A", True if gui.GET_PLATFORM() == gui.MAC else False), ('Clear Clipboard', lambda e: self._copyAndPasteHelper("Clear Clipboard"), None, False) ] for (txt, cmd, sc, bind) in eList: acc = None if sc is None else shortcut + sc self.addMenuItem("EDIT", txt, cmd, shortcut=acc, createBinding=bind) # add a clear option self.addMenuSeparator("EDIT") self.addMenuItem("EDIT", "Clear All", lambda e: self._copyAndPasteHelper("Clear All")) self.addMenuSeparator("EDIT") self.addMenuItem("EDIT", 'Undo', lambda e: self._copyAndPasteHelper("Undo"), shortcut=shortcut + "Z", createBinding=False) self.addMenuItem("EDIT", 'Redo', lambda e: self._copyAndPasteHelper( "Redo"), shortcut=shortcut+"Shift-Z", createBinding=True) self.addMenuSeparator("EDIT") self.addMenuItem("EDIT", "Bold", lambda e: self._copyAndPasteHelper("BOLD"), shortcut=shortcut+"B") self.addMenuItem("EDIT", "Italic", lambda e: self._copyAndPasteHelper("ITALIC"), shortcut=shortcut+"I") self.addMenuItem("EDIT", "Underline", lambda e: self._copyAndPasteHelper("UNDERLINE"), shortcut=shortcut+"U") self.addMenuItem("EDIT", "Bold & Italic", lambda e: self._copyAndPasteHelper("BOLD_ITALIC"), shortcut=shortcut+"Shift-B") self.disableMenu("EDIT") def _editMenuSetter(self, enabled=True): if enabled: self.addMenuEdit() else: self.disableMenuEdit() def _editMenuGetter(self): return self.copyAndPaste.inUse editMenu = property(_editMenuGetter, _editMenuSetter) def appJarAbout(self, menu=None): self.infoBox("About appJar", "---\n" + __copyright__ + "\n" + "---\n\t" + gui.SHOW_VERSION().replace("\n", "\n\t") + "\n" + "---\n" + gui.SHOW_PATHS() + "\n" + "---") def appJarHelp(self, menu=None): self.infoBox("appJar Help", "For help, visit " + __url__) def addAppJarMenu(self): if self.platform == self.MAC: self.addMenuItem("MAC_APP", "About appJar", self.appJarAbout) self.addMenuWindow() self.addMenuHelp(self.appJarHelp) elif self.platform == self.WINDOWS: self.addMenuSeparator('WIN_SYS') self.addMenuItem("WIN_SYS", "About appJar", self.appJarAbout) self.addMenuItem("WIN_SYS", "appJar Help", self.appJarHelp) ##################################### # FUNCTIONS for status bar ##################################### def removeStatusbarField(self, field): if self.hasStatus and field < len(self._statusFields): self._statusFields[field].pack_forget() self._statusFields[field].destroy() del self._statusFields[field] else: raise ItemLookupError("Invalid field number for statusbar: " + str(field)) def removeStatusbar(self): if self.hasStatus: while len(self._statusFields) > 0: self.removeStatusbarField(0) self.statusFrame.pack_forget() self.statusFrame.destroy() self.hasStatus = False self.header = "" def status(self, *args, **kwargs): self.statusbar(*args, **kwargs) def statusbar(self, *args, **kwargs): """ simpleGUI - shortener for statusbar """ bg = kwargs.pop('bg', None) fg = kwargs.pop('fg', None) width = kwargs.pop('width', None) text = kwargs.pop('text', "") header = kwargs.pop('header', None) fields = kwargs.pop('fields', 1) field = kwargs.pop('field', 0) side = kwargs.pop('side', None) if not self.hasStatus: self.addStatusbar(header=header, fields=fields, side=side) self.setStatusbar(text=text) else: if len(args) > 0: text = args[0] if len(args) > 1: field = args[1] if header is not None: self.setStatusbarHeader(header) self.setStatusbar(text=text, field=field) if bg is not None: self.setStatusbarBg(bg) if fg is not None: self.setStatusbarFg(fg) if width is not None: self.setStatusbarWidth(width) def addStatusbar(self, header="", fields=1, side=None): if not self.hasStatus: class Statusbar(Frame, object): def __init__(self, master, **kwargs): super(Statusbar, self).__init__(master, **kwargs) self.hasStatus = True self.header = header self.statusFrame = Statusbar(self.appWindow) self.statusFrame.config(bd=1, relief=SUNKEN) self.statusFrame.pack(side=BOTTOM, fill=X, anchor=S) self._statusFields = [] for i in range(fields): self._statusFields.append(Label(self.statusFrame)) self._statusFields[i].config( bd=1, relief=SUNKEN, anchor=W, font=self._statusFont, width=10) self._addTooltip(self._statusFields[i], "Status bar", True) if side == "LEFT": self._statusFields[i].pack(side=LEFT) elif side == "RIGHT": self._statusFields[i].pack(side=RIGHT) else: self._statusFields[i].pack(side=LEFT, expand=1, fill=BOTH) else: self.error("Statusbar already exists - ignoring") def setStatusbarHeader(self, header): if self.hasStatus: self.header = header def setStatusbar(self, text, field=0): if self.hasStatus: if field is None: for status in self._statusFields: status.config(text=self._getFormatStatus(text)) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(text=self._getFormatStatus(text)) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1)) def setStatusbarBg(self, colour, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(background=colour) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(background=colour) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1)) def setStatusbarFg(self, colour, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(foreground=colour) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(foreground=colour) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1)) def setStatusbarWidth(self, width, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(width=width) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(width=width) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1)) def clearStatusbar(self, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(text=self._getFormatStatus("")) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(text=self._getFormatStatus("")) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1)) # formats the string shown in the status bar def _getFormatStatus(self, text): text = str(text) if len(text) == 0: return "" elif self.header is None or len(self.header) == 0: return text else: return self.header + ": " + text ##################################### # TOOLTIPS ##################################### def _addTooltip(self, item, text, hideWarn=False): self._loadTooltip() if not ToolTip: if not hideWarn: self.warn("ToolTips unavailable - check tooltip.py is in the lib folder") elif text == "": self._disableTooltip(item) else: # turn off warnings about tooltips with PauseLogger(): # if there's already tt, just change it if hasattr(item, "tt_var"): item.tt_var.set(text) # otherwise create one else: var = StringVar(self.topLevel) var.set(text) tip = ToolTip(item, delay=500, follow_mouse=1, textvariable=var) item.tooltip = tip item.tt_var = var return item.tt_var def _enableTooltip(self, item): if hasattr(item, "tooltip"): item.tooltip.configure(state="normal") else: self.warn("Unable to enable tooltip - none present.") def _disableTooltip(self, item): if hasattr(item, "tooltip"): item.tooltip.configure(state="disabled") else: self.warn("Unable to disable tooltip - none present.") ##################################### # FUNCTIONS to show pop-up dialogs ##################################### def popUp(self, title, message=None, kind="info", parent=None): """ simpleGUI - shortener for the various popUps """ if message is None: message = title title = kind.capitalize() + " Dialog" if kind == "info": return self.infoBox(title, message, parent) elif kind == "error": return self.errorBox(title, message, parent) elif kind == "warning": return self.warningBox(title, message, parent) elif kind == "yesno": return self.yesNoBox(title, message, parent) elif kind == "question": return self.questionBox(title, message, parent) elif kind == "ok": return self.okBox(title, message, parent) elif kind == "retry": return self.retryBox(title, message, parent) elif kind == "string": return self.stringBox(title, message, parent) elif kind == "integer": return self.integerBox(title, message, parent) elif kind == "float": return self.floatBox(title, message, parent) elif kind == "text": return self.textBox(title, message, parent) elif kind == "number": return self.numberBox(title, message, parent) else: gui.error("Invalid popUp kind: %s, with title: %s", kind, title) def prompt(self, title, message, kind="string", parent=None): return self.popUp(title, message, kind, parent) # function to access the last made pop_up def getPopUp(self): return self.topLevel.POP_UP def infoBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: MessageBox.showinfo(title, message) if self.topLevel.displayed: self._bringToFront() else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} MessageBox.showinfo(title, message, **opts) self._bringToFront(parent) def errorBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: MessageBox.showerror(title, message) if self.topLevel.displayed: self._bringToFront() else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} MessageBox.showerror(title, message, **opts) self._bringToFront(parent) def warningBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: MessageBox.showwarning(title, message) if self.topLevel.displayed: self._bringToFront() else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} MessageBox.showwarning(title, message, **opts) self._bringToFront(parent) def yesNoBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return MessageBox.askyesno(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return MessageBox.askyesno(title=title, message=message, **opts) def stringBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return SimpleDialog.askstring(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return SimpleDialog.askstring(title=title, message=message, **opts) def integerBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return SimpleDialog.askinteger(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return SimpleDialog.askinteger(title=title, message=message, **opts) def floatBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return SimpleDialog.askfloat(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return SimpleDialog.askfloat(title=title, message=message, **opts) def questionBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return True if MessageBox.askquestion(title, message).lower() == "yes" else False else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return True if MessageBox.askquestion(title, message, **opts).lower() == "yes" else False def okBox(self, title, message, parent=None): self.topLevel.update_idletasks() title, message = self._translatePopup(title, message) if parent is None: return MessageBox.askokcancel(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return MessageBox.askokcancel(title, message, **opts) def retryBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return MessageBox.askretrycancel(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return MessageBox.askretrycancel(title, message, **opts) def openBox(self, title=None, dirName=None, fileTypes=None, asFile=False, parent=None, multiple=False, mode='r'): self.topLevel.update_idletasks() # define options for opening options = {} if title is not None: options['title'] = title if dirName is not None: options['initialdir'] = dirName if fileTypes is not None: options['filetypes'] = fileTypes if parent is not None: options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) if asFile: options["mode"] = mode if multiple: files = list(filedialog.askopenfiles(**options)) else: files = filedialog.askopenfile(**options) return files # will return "" if cancelled else: if multiple: files = list(self.topLevel.tk.splitlist(filedialog.askopenfilenames(**options))) else: files = filedialog.askopenfilename(**options) return files def saveBox( self, title=None, fileName=None, dirName=None, fileExt=".txt", fileTypes=None, asFile=False, parent=None): self.topLevel.update_idletasks() if fileTypes is None: fileTypes = [('all files', '.*'), ('text files', '.txt')] # define options for opening options = {} options['defaultextension'] = fileExt options['filetypes'] = fileTypes options['initialdir'] = dirName options['initialfile'] = fileName options['title'] = title if parent is not None: options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) if asFile: return filedialog.asksaveasfile(mode='w', **options) # will return "" if cancelled else: return filedialog.asksaveasfilename(**options) def directoryBox(self, title=None, dirName=None, parent=None): self.topLevel.update_idletasks() options = {} options['initialdir'] = dirName options['title'] = title options['mustexist'] = False if parent is not None: options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) fileName = filedialog.askdirectory(**options) if fileName == "": return None else: return fileName def colourBox(self, colour='#ff0000', parent=None): self.topLevel.update_idletasks() if parent is None: col = askcolor(colour) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} col = askcolor(colour, **opts) if col[1] is None: return None else: return col[1] def textBox(self, title="Text Box", question="Enter text", defaultValue=None, parent=None): self.topLevel.update_idletasks() if defaultValue is not None: defaultVar = StringVar(self.topLevel) defaultVar.set(defaultValue) else: defaultVar = None if parent is None: parent = self.topLevel else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) return TextDialog(parent, title, question, defaultVar=defaultVar).result def numberBox(self, title="Number Box", question="Enter a number", parent=None): return self.numBox(title, question, parent) def numBox(self, title="Number Box", question="Enter a number", parent=None): self.topLevel.update_idletasks() if parent is None: parent = self.topLevel else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) return NumDialog(parent, title, question).result ############################################################################ #### ******* ------ CLASS MAKERS FROM HERE ------ *********** ######### ############################################################################ ##################################### # Named classes for containing groups ##################################### def _makeParentBox(self): class ParentBox(frameBase, object): def __init__(self, parent, **opts): super(ParentBox, self).__init__(parent, **opts) self.setup() def setup(self): pass # customised config setters def config(self, cnf=None, **kw): self.configure(cnf, **kw) def configure(self, cnf=None, **kw): # properties to propagate to CheckBoxes kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) if "bg" in kw: for child in self.winfo_children(): gui.SET_WIDGET_BG(child, kw["bg"]) kw = self.processConfig(kw) # propagate anything left super(ParentBox, self).config(cnf, **kw) def processConfig(self, kw): return kw return ParentBox def _makeLabelBox(self): ParentBox = self._makeParentBox() class LabelBox(ParentBox): def setup(self): self.theLabel = None self.theWidget = None return LabelBox def _makeButtonBox(self): ParentBox = self._makeParentBox() class ButtonBox(ParentBox): def setup(self): self.theWidget = None self.theButton = None return ButtonBox def _makeWidgetBox(self): ParentBox = self._makeParentBox() class WidgetBox(ParentBox): def setup(self): self.theWidgets = [] return WidgetBox def makeListBoxContainer(self): ParentBox = self._makeParentBox() class ListBoxContainer(Frame, object): def __init__(self, parent, **opts): super(ListBoxContainer, self).__init__(parent) # customised config setters def config(self, cnf=None, **kw): self.configure(cnf, **kw) def configure(self, cnf=None, **kw): # properties to propagate to CheckBoxes kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) # propagate anything left super(ListBoxContainer, self).config(cnf, **kw) return ListBoxContainer ##################################### # Simple Separator ##################################### def _makeSeparator(self): class Separator(frameBase, object): def __init__(self, parent, orient="horizontal", *args, **options): super(Separator, self).__init__(parent, *args, **options) self.line = frameBase(self) self.line.SKIP_CLEANSE = True if orient == "horizontal": self.line.config(relief="ridge", height=2, width=100, borderwidth=1) self.line.pack(padx=5, pady=5, fill="x", expand=1) else: self.line.config(relief="ridge", height=100, width=2, borderwidth=1) self.line.pack(padx=5, pady=5, fill="y", expand=1) def config(self, cnf=None, **kw): self.configure(cnf, **kw) def configure(self, cnf=None, **kw): if "fg" in kw: self.line.config(bg=kw.pop("fg")) super(Separator, self).config(cnf, **kw) return Separator ##################################### # Drag Grip Label Class ##################################### def _makeGrip(self): class Grip(labelBase, object): gray25 = BitmapImage(data=""" #define im_width 16 #define im_height 16 static char im_bits[] = { 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, 0x88, 0x88, 0x22, 0x22, }; """) def __init__(self, *args, **kwargs): super(Grip, self).__init__(image=self.gray25, *args, **kwargs) self.config(cursor="fleur", anchor=CENTER) self.bind("<ButtonPress-1>", self.StartMove) self.bind("<ButtonRelease-1>", self.StopMove) self.bind("<B1-Motion>", self.OnMotion) def StartMove(self, event): self.x = event.x self.y = event.y def StopMove(self, event): self.x = None self.y = None def OnMotion(self, event): parent = self.winfo_toplevel() deltax = event.x - self.x deltay = event.y - self.y x = parent.winfo_x() + deltax y = parent.winfo_y() + deltay parent.geometry("+%s+%s" % (x, y)) return Grip ##################################### # Hyperlink Class ##################################### @staticmethod def _makeLink(): class Link(labelBase, object): def __init__(self, *args, **kwargs): self.useTtk = kwargs.pop('useTtk',False) super(Link, self).__init__(*args, **kwargs) self.fg = "#0000ff" self.overFg="#3366ff" if not self.useTtk: self.config(fg=self.fg, takefocus=1)#, highlightthickness=0) else: self.config(style="Link.TLabel") self.DEFAULT_TEXT = "" if gui.GET_PLATFORM() == gui.MAC: self.config(cursor="pointinghand") elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.config(cursor="hand2") self.bind("<Enter>", self.enter) self.bind("<Leave>", self.leave) def enter(self, e): if self.useTtk: self.config(style="LinkOver.TLabel") else: super(Link, self).config(fg=self.overFg) def leave(self, e): if self.useTtk: self.config(style="Over.TLabel") else: super(Link, self).config(fg=self.fg) def registerCallback(self, callback): self.bind("<Button-1>", callback) self.bind("<Return>", callback) self.bind("<space>", callback) def launchBrowser(self, event): webbrowser.open_new(r"" + self.page) # webbrowser.open_new_tab(self.page) def registerWebpage(self, page): if not page.startswith("http"): raise InvalidURLError("Invalid URL: " + page + " (it should begin as http://)") self.page = page self.bind("<Button-1>", self.launchBrowser) self.bind("<Return>", self.launchBrowser) self.bind("<space>", self.launchBrowser) def config(self, **kw): self.configure(**kw) def configure(self, **kw): kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) if "text" in kw: self.DEFAULT_TEXT = kw["text"] if 'fg' in kw: self.fg = kw['fg'] self.overFg = gui.TINT(self, self.fg) super(Link, self).config(**kw) def cget(self, option): if option == "text" and hasattr(self, 'page'): return self.page return super(Link, self).cget(option) return Link ####################### # Upgraded scale - http://stackoverflow.com/questions/42843425/change-trough-increment-in-python-tkinter-scale-without-affecting-slider/ ####################### def _makeAjScale(self): class AjScale(scaleBase, object): '''a scale where a trough click jumps by a specified increment instead of the resolution''' def __init__(self, master=None, **kwargs): self.increment = kwargs.pop('increment',1) super(AjScale, self).__init__(master, **kwargs) self.bind('<Button-1>', self.jump) def jump(self, event): clicked = self.identify(event.x, event.y) return self._jump(clicked) def _jump(self, clicked): if clicked == 'trough1': self.set(self.get() - self.increment) elif clicked == 'trough2': self.set(self.get() + self.increment) else: return None return 'break' return AjScale ##################################### # appJar Frame ##################################### def _makeAjFrame(self): class ajFrame(frameBase, object): def __init__(self, parent, *args, **options): super(ajFrame, self).__init__(parent, *args, **options) return ajFrame ######################### # Class to provide auto-completion on Entry boxes # inspired by: https://gist.github.com/uroshekic/11078820 ######################### def _makeAutoCompleteEntry(self): ### Create the dynamic class class AutoCompleteEntry(entryBase, object): def __init__(self, words, tl, *args, **kwargs): super(AutoCompleteEntry, self).__init__(*args, **kwargs) self.allWords = words self.allWords.sort() self.topLevel = tl # store variable - so we can see when it changes self.var = self["textvariable"] = StringVar() self.var.auto_id = self.var.trace('w', self.textChanged) # register events self.bind("<Right>", self.selectWord) self.bind("<Return>", self.selectWord) self.bind("<Up>", self.moveUp) self.bind("<Down>", self.moveDown) self.bind("<FocusOut>", self.closeList, add="+") self.bind("<Escape>", self.closeList, add="+") # no list box - yet self.listBoxShowing = False self.rows = 10 # customised config setters def config(self, cnf=None, **kw): self.configure(cnf, **kw) def configure(self, cnf=None, **kw): kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) if "font" in kw: self.listFont = kw["font"] # propagate anything left super(AutoCompleteEntry, self).config(cnf, **kw) def removeWord(self, word): if word in self.allWords: self.allWords.remove(word) def addWords(self, words): if not hasattr(words, "__iter__"): words = [words] for word in words: if word not in self.allWords: self.allWords.append(word) self.allWords.sort() def changeWords(self, words): self.allWords = words self.allWords.sort() def setNumRows(self, rows): self.rows = rows # function to see if words match def checkMatch(self, fieldValue, acListEntry): pattern = re.compile(re.escape(fieldValue) + '.*', re.IGNORECASE) return re.match(pattern, acListEntry) # function to get all matches as a list def getMatches(self): return [w for w in self.allWords if self.checkMatch(self.var.get(), w)] # called when typed in entry def textChanged(self, name, index, mode): # if no text - close list if self.var.get() == '': self.closeList() else: if not self.listBoxShowing: self.makeListBox() self.popListBox() # add words to the list def popListBox(self): if self.listBoxShowing: self.listbox.delete(0, END) shownWords = self.getMatches() if shownWords: for w in shownWords: self.listbox.insert(END, w) self.selectItem(0) # function to create & show an empty list box def makeListBox(self): self.listbox = Listbox(self.topLevel, width=self["width"]-8, height=8) self.listbox.config(height=self.rows) # self.listbox.config(bg=self.cget("bg"), selectbackground=self.cget("selectbackground")) # self.listbox.config(fg=self.cget("fg")) if hasattr(self, "listFont"): self.listbox.config(font=self.listFont) self.listbox.bind("<Button-1>", self.mouseClickBox) self.listbox.bind("<Right>", self.selectWord) self.listbox.bind("<Return>", self.selectWord) x = self.winfo_rootx() - self.topLevel.winfo_rootx() y = self.winfo_rooty() - self.topLevel.winfo_rooty() + self.winfo_height() self.listbox.place(x=x, y=y) self.listBoxShowing = True # function to handle a mouse click in the list box def mouseClickBox(self, e=None): self.selectItem(self.listbox.nearest(e.y)) self.selectWord(e) # function to close/delete list box def closeList(self, event=None): if self.listBoxShowing: self.listbox.destroy() self.listBoxShowing = False # copy word from list to entry, close list def selectWord(self, event): if self.listBoxShowing: self.var.set(self.listbox.get(ACTIVE)) self.icursor(END) self.closeList() return "break" # wrappers for up/down arrows def moveUp(self, event): return self.arrow("UP") def moveDown(self, event): return self.arrow("DOWN") # function for handling up/down keys def arrow(self, direction): if not self.listBoxShowing: self.makeListBox() self.popListBox() curItem = 0 numItems = self.listbox.size() else: numItems = self.listbox.size() curItem = self.listbox.curselection() if curItem == (): curItem = -1 else: curItem = int(curItem[0]) if direction == "UP" and curItem > 0: curItem -= 1 elif direction == "UP" and curItem <= 0: curItem = numItems - 1 elif direction == "DOWN" and curItem < numItems - 1: curItem += 1 elif direction == "DOWN" and curItem == numItems - 1: curItem = 0 self.selectItem(curItem) # stop the event propgating return "break" # function to select the specified item def selectItem(self, position): numItems = self.listbox.size() self.listbox.selection_clear(0, numItems - 1) self.listbox.see(position) # Scroll! self.listbox.selection_set(first=position) self.listbox.activate(position) # return the dynamic class return AutoCompleteEntry ##################################### # Tree Widget Class # https://www.safaribooksonline.com/library/view/python-cookbook-2nd/0596007973/ch11s11.html # idlelib -> TreeWidget.py # https://svn.python.org/projects/python/trunk/Lib/idlelib/TreeWidget.py # modify minidom - https://wiki.python.org/moin/MiniDom ##################################### def _makeAjTreeNode(self): class AjTreeNode(TreeNode, object): def __init__(self, canvas, parent, item): super(AjTreeNode, self).__init__(canvas, parent, item) self.hasAttr = False self.showAttr = False self.bgColour = None self.fgColour = None self.bgHColour = None self.fgHColour = None # called (if set) when a leaf is edited self.editEvent = None if self.parent: self.bgColour = self.parent.bgColour self.fgColour = self.parent.fgColour self.bgHColour = self.parent.bgHColour self.fgHColour = self.parent.fgHColour self.editEvent = self.parent.editEvent self.showAttr = self.parent.showAttr else: # set this once, in parent self.canvas.menu = None self.canvas.lastSelected = None self.menuBound = False # customised config setters def config(self, cnf=None, **kw): self.configure(cnf, **kw) def configure(self, cnf=None, **kw): # properties to propagate to CheckBoxes kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) if "bg" in kw: self.setBgColour(kw.pop("bg")) if "fg" in kw: self.setFgColour(kw.pop("fg")) # # propagate anything left # super(AjTreeNode, self).config(cnf, **kw) # NOT COMPLETE def addChild(self, child): child = self.__class__(self.canvas, self, child) self.children.append(child) self.update() def registerEditEvent(self, func): self.editEvent = func for c in self.children: c.registerEditEvent(func) def showAttributes(self, show): self.showAttr = show for c in self.children: c.showAttributes(show) self.update() def showMenu(self, show): if show: if self.canvas.menu is None: self.canvas.menu = Menu(self.canvas, tearoff=0) self.canvas.menu.add_command(label="delete", command=self._delete) self.canvas.menu.bind("<FocusOut>", lambda e: self.canvas.menu.unpost()) self._bindMenu() else: # need to go through and unbind... pass def setBgColour(self, colour): self.canvas.config(background=colour) self.bgColour = colour self._doUpdateColour() def setFgColour(self, colour): self.fgColour = colour self._doUpdateColour() def setBgHColour(self, colour): self.bgHColour = colour self._doUpdateColour() def setFgHColour(self, colour): self.fgHColour = colour self._doUpdateColour() def setAllColours(self, bg=None, fg=None, bgH=None, fgH=None): if bg is not None: self.canvas.config(background=bg) self.bgColour = bg if fg is not None: self.fgColour = fg if bgH is not None: self.bgHColour = bgH if fgH is not None: self.fgHColour = fgH self._doUpdateColour() def _doUpdateColour(self): self._updateColours(self.bgColour, self.bgHColour, self.fgColour, self.fgHColour) self.update() def _updateColours(self, bgCol, bgHCol, fgCol, fgHCol): self.bgColour = bgCol self.fgColour = fgCol self.bgHColour = bgHCol self.fgHColour = fgHCol for c in self.children: c._updateColours(bgCol, bgHCol, fgCol, fgHCol) def draw(self, x, y): cy = super(AjTreeNode, self).draw(x, y) self._bindMenu() return cy # override parent function, so that we can change the label's background colour def drawtext(self): attr=self.item.node.attributes self.hasAttr = self.showAttr and attr is not None and len(attr) > 0 if self.hasAttr: self.attrId = self.canvas.create_text(self.x+20-1, self.y-1, anchor="nw", text='*') self.x += 7 super(AjTreeNode, self).drawtext() if self.hasAttr: self.x -= 7 self.colourLabels() # add a tooltip for attributes if ToolTip is not False and self.hasAttr: text = "Attributes\n" for key, val in attr.items(): text += " " + key + ":" + val + "\n" text = text[:-1] ToolTip(self.label, text, delay=500, follow_mouse=1) ToolTip(self.canvas, text, specId=self.attrId, delay=500, follow_mouse=1) def _bindMenu(self): if self.canvas.menu is not None and not self.menuBound: self.menuBound = True if gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.canvas.tag_bind(self.image_id, "<Button-3>", self._showMenu) if self.hasAttr: self.canvas.tag_bind(self.attrId, "<Button-3>", self._showMenu) self.label.bind("<Button-3>", self._showMenu) else: self.canvas.tag_bind(self.image_id, "<Button-2>", self._showMenu) if self.hasAttr: self.canvas.tag_bind(self.attrId, "<Button-2>", self._showMenu) self.label.bind("<Button-2>", self._showMenu) # override parent function, so that we can change the label's background colour def drawicon(self): super(AjTreeNode, self).drawicon() def _showMenu(self, event=None): self.canvas.lastSelected = event.widget self.canvas.menu.focus_set() self.canvas.menu.post(event.x_root - 10, event.y_root - 10) return "break" def _delete(self): self.update() self.canvas.lastSelected.destroy() # override parent function, so that we can generate an event on finish editing def edit_finish(self, event=None): super(AjTreeNode, self).edit_finish(event) if self.editEvent is not None: self.editEvent() def colourLabels(self): if self.showAttr and self.hasAttr: self.canvas.itemconfigure(self.attrId, fill=self.fgColour) try: if not self.selected: self.label.config(background=self.bgColour, fg=self.fgColour) else: self.label.config(background=self.bgHColour, fg=self.fgHColour) except: pass def getSelectedText(self): item = self.getSelected() if item is not None: return item.GetText(), item.getAttribute() else: return None def getSelected(self): if self.selected: return self.item else: for c in self.children: val = c.getSelected() if val is not None: return val return None return AjTreeNode def _makeAjTreeData(self): # implementation of container for XML data # functions implemented as specified in skeleton class AjTreeData(TreeItem, object): def __init__(self, document): # handle root node try: self.node = document.documentElement except AttributeError: self.node = document self.dblClickFunc = None self.clickFunc = None self.treeTitle = None self.canEdit = True # REQUIRED FUNCTIONS # called whenever the tree expands def GetText(self): node = self.node if node.nodeType == node.ELEMENT_NODE: return node.nodeName elif node.nodeType == node.TEXT_NODE: return node.nodeValue def getAttribute(self, att='id'): try: return self.node.attributes[att].value except: return None def IsEditable(self): return self.canEdit and not self.node.hasChildNodes() def SetText(self, text): self.node.replaceWholeText(text) def IsExpandable(self): return self.node.hasChildNodes() def GetIconName(self): if self.clickFunc is not None: self.clickFunc(self.treeTitle, self.getAttribute()) if not self.IsExpandable(): return "python" # change to file icon def GetSubList(self): children = self.node.childNodes prelist = [AjTreeData(node) for node in children] itemList = [item for item in prelist if item.GetText().strip()] for item in itemList: item.registerDblClick(self.treeTitle, self.dblClickFunc) item.registerClick(self.treeTitle, self.clickFunc) item.canEdit = self.canEdit return itemList def OnDoubleClick(self): if self.IsEditable(): # TO DO: start editing this node... pass if self.dblClickFunc is not None: self.dblClickFunc(self.treeTitle, self.getAttribute()) # Â EXTRA FUNCTIONS # TODO: can only set before calling go() def setCanEdit(self, value=True): self.canEdit = value # TODO: can only set before calling go() def registerDblClick(self, title, func): self.treeTitle = title self.dblClickFunc = func # TODO: can only set before calling go() def registerClick(self, title, func): self.treeTitle = title self.clickFunc = func # not used - for DEBUG def getSelected(self, spaces=1): if spaces == 1: gui.trace("%s", self.node.tagName) for c in self.node.childNodes: if gui.GET_WIDGET_CLASS(c) == "Element": gui.trace("%s >> %s", " "*spaces, c.tagName) node = AjTreeData(c) node.getSelected(spaces + 2) elif gui.GET_WIDGET_CLASS(c) == "Text": val = c.data.strip() if len(val) > 0: gui.trace("%s >>>> %s", " "*spaces, val) return AjTreeData
Ancestors (in MRO)
- gui
- builtins.object
Class variables
var BASIC_NOTES
var DURATIONS
var E
var FLAT
var GROOVE
var LEFT
var LINUX
var MAC
var N
var NE
var NOTES
var NW
var RAISED
var RIDGE
var RIGHT
var S
var SE
var SUNKEN
var SW
var W
var WINDOWS
var bg
var built
var colspan
var editMenu
var enterKey
var exe_file
var exe_path
var expand
var fastStop
var fg
var font
var fonts
var fullscreen
var guiPadding
var icon
var inPadding
var inputFont
var instantiated
var labelFont
var language
var lib_file
var lib_path
var location
var logFile
var logLevel
var padding
var randomColour
var resizable
var row
var rowspan
var size
var startFunction
var statusFont
var sticky
var stopFunction
var stretch
var title
var top
var transparency
var ttkTheme
var visible
Static methods
def __init__(
self, title=None, geom=None, handleArgs=True, language=None, startWindow=None, useTtk=False, useSettings=False, showIcon=True, **kwargs)
constructor - sets up the empty GUI window, and inits the various properties
def __init__( self, title=None, geom=None, handleArgs=True, language=None, startWindow=None, useTtk=False, useSettings=False, showIcon=True, **kwargs ): """ constructor - sets up the empty GUI window, and inits the various properties """ if self.__class__.instantiated: raise Exception("You cannot have more than one instance of gui, try using a subWindow.") else: self.__class__.instantiated = True self.alive = True # first up, set the logger def _logForLevel(self, message, *args, **kwargs): if self.isEnabledFor(logging.DEBUG-5): self._log(logging.DEBUG-5, message, args, **kwargs) def _logToRoot(message, *args, **kwargs): logging.log(logging.DEBUG-5, message, *args, **kwargs) logging.basicConfig(level=logging.WARNING, format='%(asctime)s %(name)s:%(levelname)s %(message)s') logging.addLevelName(logging.DEBUG - 5, 'TRACE') setattr(logging, 'TRACE', logging.DEBUG -5) setattr(logging.getLoggerClass(), "trace", _logForLevel) setattr(logging, "trace", _logToRoot) logFile = kwargs.pop("file", kwargs.pop("logFile", None)) logLevel = kwargs.pop("log", kwargs.pop("logLevel", None)) self._language = language self.useSettings = useSettings self.settingsFile = "appJar.ini" self.externalSettings = {} self.startWindow = startWindow # check any command line arguments if argparse is None: handleArgs = False args = self._handleArgs() if handleArgs else None # warn if we're in an untested mode self._checkMode() # first out, verify the platform self.platform = gui.GET_PLATFORM() # process any command line arguments self.ttkFlag = False selectedTtkTheme = None if handleArgs: if args.f: gui.setLogFile(args.f) logFile = None # don't use any param logFile tmplevel, logLevel = logLevel, None if args.c: gui.setLogLevel("CRITICAL") elif args.e: gui.setLogLevel("ERROR") elif args.w: gui.setLogLevel("WARNING") elif args.i: gui.setLogLevel("INFO") elif args.d: gui.setLogLevel("DEBUG") elif args.t: gui.setLogLevel("TRACE") else: loglevel = tmplevel if logFile is not None: gui.setLogFile(logFile) if logLevel is not None: gui.setLogLevel(logLevel) if handleArgs: if args.l: self._language = args.l if args.ttk: useTtk = True if args.ttk is not True: selectedTtkTheme = args.ttk if args.s: self.useSettings = True if args.s is not True: self.settingsFile = args.s # configure as ttk if useTtk: self._useTtk() if useTtk is not True: selectedTtkTheme = useTtk # a stack to hold containers as being built # done here, as initArrays is called elsewhere - to reset the gubbins self.containerStack = [] self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}} # first up, set up all the data stores self.widgetManager = WidgetManager() self.accessMade = False # accessibility subWindow self.splashConfig = None # splash screen? self.dnd = None # the dnd manager self.doFlash = False # set up flash variable self.hasTitleBar = True # used to hide/show title bar # validate function callbacks - used by numeric texts # created first time a widget is used self.validateNumeric = None self.validateSpinBox = None # dynamically create lots of functions for configuring stuff self._buildConfigFuncs() # language parser self.configParser = None # set up some default path locations # this fails if in interactive mode.... try: gui.exe_file = str(os.path.basename(theMain.__file__)) gui.exe_path = str(os.path.dirname(theMain.__file__)) except: pass gui.lib_file = os.path.abspath(__file__) gui.lib_path = os.path.dirname(gui.lib_file) # location of appJar self.resource_path = os.path.join(gui.lib_path, "resources") self.icon_path = os.path.join(self.resource_path, "icons") self.sound_path = os.path.join(self.resource_path, "sounds") self.appJarIcon = os.path.join(self.icon_path, "favicon.ico") # user configurable self.userImages = gui.exe_path self.userSounds = gui.exe_path # create the main window - topLevel self.topLevel = Tk() self.topLevel.bind('<Configure>', self._windowEvent) def _setFocus(e): try: e.widget.focus_set() except: pass # these are specifically to make right-click menus disapear on linux self.topLevel.bind('<Button-1>', lambda e: _setFocus(e)) self.topLevel.bind('<Button-2>', lambda e: _setFocus(e)) self.topLevel.bind('<Button-3>', lambda e: _setFocus(e)) # override close button self.topLevel.protocol("WM_DELETE_WINDOW", self.stop) # temporarily hide it self.topLevel.withdraw() # used to keep a handle on the last pop-up dialog # allows the dialog to be closed remotely # mainly for test-automation self.topLevel.POP_UP = None # create a frame to store all the widgets #Â now a canvas to allow animation... self.appWindow = CanvasDnd(self.topLevel) self.appWindow.pack(fill=BOTH, expand=True) self.topLevel.canvasPane = self.appWindow # set the windows title if title is None: title = "appJar" if gui.exe_file is None else gui.exe_file self.setTitle(title) self.topLevel.winIcon = None # will store the path to any icon # configure the geometry of the window self.topLevel.escapeBindId = None # used to exit fullscreen self.topLevel.stopFunction = None # used to exit fullscreen self.topLevel.startFunction = None # set the resize status - default to True self.topLevel.locationSet = False self.topLevel.ignoreSettings = False self.topLevel.isFullscreen = False # records if we're in fullscreen - stops hideTitle from breaking self.topLevel.displayed = True if geom is not None: self.setSize(geom) self.setResizable(True) self.Widgets = WIDGET_NAMES # 3 fonts used for most widgets self._buttonFont = tkFont.Font(family="Helvetica", size=12,) self._labelFont = tkFont.Font(family="Helvetica", size=12) self._inputFont = tkFont.Font(family="Helvetica", size=12) self._statusFont = tkFont.Font(family="Helvetica", size=12) # dedicated font for access widget self._accessFont = tkFont.Font(family="Arial", size=11,) # dedicated font for links - forces bold & underlined, but updated with label fonts self._linkFont = tkFont.Font(family="Helvetica", size=12, weight='bold', underline=1) self.tableFont = tkFont.Font(family="Helvetica", size=12) # create a menu bar - only shows if populated # now created in menu functions, as it generated a blank line... self.hasMenu = False self.hasStatus = False self.copyAndPaste = CopyAndPaste(self.topLevel, self) class Toolbar(frameBase, object): def __init__(self, master, **kwargs): super(Toolbar, self).__init__(master, **kwargs) self.BG_COLOR = None self.pinned = True self.pinBut = None self.inUse = False self.toolbarMin = None self.location = None def makeMinBar(self): if self.toolbarMin is None: self.toolbarMin = Frame(self.master, bd=1, relief=RAISED) self.toolbarMin.config(bg="gray", height=3) self.bind("<Leave>", self._minToolbar) self.toolbarMin.bind("<Enter>", self._maxToolbar) def hide(self): if self.inUse: self.pack_forget() if self.toolbarMin is not None: self.toolbarMin.pack_forget() def show(self): if self.inUse: self.pack(before=self.location, side=TOP, fill=X) if self.toolbarMin is not None: self.toolbarMin.pack_forget() def _minToolbar(self, e=None): if not self.pinned: if self.toolbarMin is not None: self.toolbarMin.config(width=self.winfo_reqwidth()) self.toolbarMin.pack(before=self.location, side=TOP, fill=X) self.pack_forget() def _maxToolbar(self, e=None): self.pack(before=self.location, side=TOP, fill=X) if self.toolbarMin is not None: self.toolbarMin.pack_forget() class WidgetContainer(frameBase, object): def __init__(self, master, **kwargs): super(WidgetContainer, self).__init__(master, **kwargs) # create the main container for this GUI container = WidgetContainer(self.appWindow) # container = Label(self.appWindow) # made as a label, so we can set an # image if not self.ttkFlag: container.config(padx=2, pady=2, background=self.topLevel.cget("bg")) container.pack(fill=BOTH, expand=True) self._addContainer("root", WIDGET_NAMES.RootPage, container, 0, 1) self.tb = Toolbar(self.appWindow) if not self.ttkFlag: self.tb.config(bd=1, relief=RAISED) else: self.tb.config(style="Toolbar.TFrame") # set up the main container to be able to host an image self._configBg(container) if self.platform == self.WINDOWS and showIcon: try: self.setIcon(self.appJarIcon) except: # file not found gui.trace("Error setting Windows default icon") # set the ttk theme if self.ttkFlag: self.setTtkTheme(selectedTtkTheme) # for configuting event processing self.EVENT_SIZE = 1000 self.EVENT_SPEED = 100 self.preloadAnimatedImageId = None self.processQueueId = None # an array to hold any threaded events.... self.events = [] self.pollTime = 250 self._fastStop = False self.configure(**kwargs) # special bindings self._globalBindings() self.built = True
def CENTER(
win, up=0)
@staticmethod def CENTER(win, up=0): gui.SET_LOCATION("CENTER", win=win, up=up)
def CLEAN_CONFIG_DICTIONARY(
**kw)
Used by all Classes to tidy up dictionaries passed into config functions Allows us to more quickly process the dictionaries when overriding config
@staticmethod def CLEAN_CONFIG_DICTIONARY(**kw): """ Used by all Classes to tidy up dictionaries passed into config functions Allows us to more quickly process the dictionaries when overriding config """ try: kw['bg'] = kw.pop('background') except: pass try: kw['fg'] = kw.pop('foreground') except: pass kw = dict((k.lower().strip(), v) for k, v in kw.items()) return kw
def GET_DIMS(
container)
returns a dictionary of dimensions for the supplied container
@staticmethod def GET_DIMS(container): """ returns a dictionary of dimensions for the supplied container """ container.update() dims = {} # get the apps requested width & height dims["r_width"] = container.winfo_reqwidth() dims["r_height"] = container.winfo_reqheight() # get the current width & height dims["w_width"] = container.winfo_width() dims["w_height"] = container.winfo_height() # get the window's width & height dims["s_width"] = container.winfo_screenwidth() dims["s_height"] = container.winfo_screenheight() # determine best geom for OS # on MAC & LINUX, w_width/w_height always 1 unless user-set # on WIN, w_height is bigger then r_height - leaving empty space if gui.GET_PLATFORM() in [gui.MAC, gui.LINUX]: if dims["w_width"] != 1: dims["b_width"] = dims["w_width"] dims["b_height"] = dims["w_height"] else: dims["b_width"] = dims["r_width"] dims["b_height"] = dims["r_height"] else: dims["b_height"] = max(dims["r_height"], dims["w_height"]) dims["b_width"] = max(dims["r_width"], dims["w_width"]) # GUI's corner - widget's corner # widget's corner can be 0 on windows when size not set by user dims["outerFrameWidth"] = 0 if container.winfo_x() == 0 else container.winfo_rootx() - container.winfo_x() dims["titleBarHeight"] = 0 if container.winfo_rooty() == 0 else container.winfo_rooty() - container.winfo_y() # add it all together dims["actualWidth"] = dims["b_width"] + (dims["outerFrameWidth"] * 2) dims["actualHeight"] = dims["b_height"] + dims["titleBarHeight"] + dims["outerFrameWidth"] dims["x"] = (dims["s_width"] // 2) - (dims["actualWidth"] // 2) dims["y"] = (dims["s_height"] // 2) - (dims["actualHeight"] // 2) return dims
def GET_PLATFORM(
)
returns one of the gui class's three static platform variables
@staticmethod def GET_PLATFORM(): """ returns one of the gui class's three static platform variables """ if platform() in ["win32", "Windows"]: return gui.WINDOWS elif platform() == "Darwin": return gui.MAC elif platform() in ["Linux", "FreeBSD"]: return gui.LINUX else: raise Exception("Unknown platform: " + platform())
def GET_WIDGET_CLASS(
widget)
@staticmethod def GET_WIDGET_CLASS(widget): return widget.__class__.__name__
def MAKE_FUNC(
funcName, param)
function to automate lambdas
@staticmethod def MAKE_FUNC(funcName, param): ''' function to automate lambdas ''' # make sure we get a function if not callable(funcName) and not hasattr(funcName, '__call__'): raise Exception("Invalid function: " + str(funcName)) # check if the function requires arguments argsList = getArgs(funcName) # if no args, or 1 arg in a bound function noArgs = len(argsList[0])==0 or (len(argsList[0])==1 and inspect.ismethod(funcName)) # if no args/varargs/kwargs then don't give the param if noArgs and argsList[1] is None and argsList[2] is None: return lambda *args: funcName() else: return lambda *args: funcName(param)
def MOUSE_POS_IN_WIDGET(
widget, event, findRoot=True)
returns the mouse's relative position in a widget :param widget: the widget to look in :param event: the event containing the mouse coordinates :param findRoot: if we should make this relative to the parent
@staticmethod def MOUSE_POS_IN_WIDGET(widget, event, findRoot=True): """ returns the mouse's relative position in a widget :param widget: the widget to look in :param event: the event containing the mouse coordinates :param findRoot: if we should make this relative to the parent """ # first we have to get the real master master = widget while findRoot: if isinstance(master, (SubWindow, Tk)): findRoot = False else: master = master.master # subtract the widget's top left corner from the root window's top corner x = event.x_root - master.winfo_rootx() y = event.y_root - master.winfo_rooty() gui.trace("<<MOUSE_POS_IN_WIDGET>> %s %s,%s", widget, x, y) return (x, y)
def PARSE_TWO_PARAMS(
x, y)
used to convert different possible x/y params to a tuple
@staticmethod def PARSE_TWO_PARAMS(x, y): """ used to convert different possible x/y params to a tuple """ if y is not None: return (x,y) else: if isinstance(x, (list, tuple)): return (x[0], x[1]) else: if isinstance(x, UNIVERSAL_STRING): x=x.strip() if "," in x: return [int(w.strip()) for w in x.split(",")] return (x, x)
def RANDOM_COLOUR(
self)
def RANDOM_COLOUR(self): return self.getRandomColour()
def SET_LOCATION(
x, y=None, ignoreSettings=None, win=None, up=0)
@staticmethod def SET_LOCATION(x, y=None, ignoreSettings=None, win=None, up=0): if ignoreSettings is not None: win.ignoreSettings = ignoreSettings if gui.GET_PLATFORM() != gui.LINUX: trans = win.attributes('-alpha') win.attributes('-alpha', 0.0) win.update_idletasks() if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre'] and y is None: x = y = 'c' else: x, y = gui.PARSE_TWO_PARAMS(x, y) gui.trace("Set location called with %s, %s", x, y) # get the window's dimensions dims = gui.GET_DIMS(win) # set any center positions if isinstance(x, UNIVERSAL_STRING) and x.lower() in ['c', 'center', 'centre']: x = dims["x"] if isinstance(y, UNIVERSAL_STRING) and y.lower() in ['c', 'center', 'centre']: y = dims["y"] # move the window up a bit if requested y = y - up if up < y else 0 # fix any out of bounds positions if x < 0 or x > dims['s_width']: x = dims['x'] if y < 0 or y > dims['s_height']: y = dims['y'] gui.trace("Screen: %sx%s. Requested: %sx%s. Location: %s, %s", dims["s_width"], dims["s_height"], dims["b_width"], dims["b_height"], x, y) win.geometry("+%d+%d" % (x, y)) win.locationSet = True if gui.GET_PLATFORM() != gui.LINUX: win.attributes('-alpha', trans)
def SET_WIDGET_BG(
widget, bg, external=False, tint=False)
@staticmethod def SET_WIDGET_BG(widget, bg, external=False, tint=False): if bg is None: # ignore empty colours return widgType = gui.GET_WIDGET_CLASS(widget) isDarwin = gui.GET_PLATFORM() == gui.MAC isLinux = gui.GET_PLATFORM() == gui.LINUX gui.trace("Config %s BG to %s", widgType, bg) # these have a highlight border to remove hideBorders = [ "Text", "AjText", "ScrolledText", "AjScrolledText", "Scale", "AjScale", "OptionMenu", "Entry", "AutoCompleteEntry", "Radiobutton", "Checkbutton", "Button"] # these shouldn't have their BG coloured by default noBg = [ "Button", "Scale", "AjScale", "Spinbox", "Listbox", "OptionMenu", "SplitMeter", "DualMeter", "Meter", "Entry", "AutoCompleteEntry", "Text", "AjText", "ScrolledText", "AjScrolledText", "ToggleFrame"] # remove the highlight borders if widgType in hideBorders: if widgType == "Entry" and widget.isValidation: pass elif widgType == "OptionMenu": widget["menu"].config(borderwidth=0) widget.config(highlightbackground=bg) if isDarwin: widget.config(background=bg) elif widgType in ["Radiobutton", "Checkbutton"]: widget.config(activebackground=bg, highlightbackground=bg) else: widget.config(highlightbackground=bg) # do some fancy tinting if external or tint: if widgType in ["Button", "Scale", "AjScale"]: widget.config(activebackground=gui.TINT(widget, bg)) elif widgType in ["Entry", "Text", "AjText", "ScrolledText", "AjScrolledText", "AutoCompleteEntry", "Spinbox"]: widget.config(selectbackground=gui.TINT(widget, bg)) widget.config(highlightcolor=gui.TINT(widget, bg)) if widgType in ["Text", "AjText", "ScrolledText", "AjScrolledText"]: widget.config(inactiveselectbackground=gui.TINT(widget, bg)) elif widgType == "Spinbox": widget.config(buttonbackground=bg) elif widgType == "Listbox": widget.config(selectbackground=gui.TINT(widget, bg)) elif widgType == "OptionMenu": widget.config(activebackground=gui.TINT(widget, bg)) widget["menu"].config(activebackground=gui.TINT(widget, bg)) elif widgType in ["Radiobutton", "Checkbutton"]: widget.config(activebackground=gui.TINT(widget, bg)) # if this is forced - change everything if external: widget.config(bg=bg) if widgType == "OptionMenu": widget["menu"].config(bg=bg) # otherwise only colour un-excluded widgets elif widgType not in noBg: widget.config(bg=bg) # deal with flash labels if widgType == "Label": widget.origBg=bg try: widget.config(fg=widget.origFg) except: pass # not a flash label # now do any of the below containers if widgType in ["LabelFrame", "PanedFrame", "Pane", "ajFrame"]: for child in widget.winfo_children(): gui.SET_WIDGET_BG(child, bg, external, tint) elif widgType == "LabelBox": # widget with label, in frame if widget.theLabel is not None: gui.SET_WIDGET_BG(widget.theLabel, bg, external, tint) gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint) elif widgType == "ButtonBox": # widget with button, in frame gui.SET_WIDGET_BG(widget.theWidget, bg, external, tint) gui.SET_WIDGET_BG(widget.theButton, bg, external, tint) elif widgType == "ListBoxContainer": #Â list box container gui.SET_WIDGET_BG(widget.lb, bg, external, tint) elif widgType == "WidgetBox": # group of buttons or labels for widg in widget.theWidgets: gui.SET_WIDGET_BG(widg, bg, external, tint)
def SET_WIDGET_FG(
widget, fg, external=False)
@staticmethod def SET_WIDGET_FG(widget, fg, external=False): widgType = gui.GET_WIDGET_CLASS(widget) gui.trace("SET_WIDGET_FG: %s - %s", widgType, fg) # only configure these widgets if external if widgType in ["Link", "Spinbox", "AjText", "AjScrolledText", "Button", "Entry", "AutoCompleteEntry"]: if external: try: #Â entry specific settings if not widget.showingDefault: widget.oldFg = fg widget.config(fg=fg) else: widget.oldFg = fg except: # other widgets widget.config(fg=fg) # handle flash labels elif widgType == "Label": widget.config(fg=fg) widget.origFg=fg try: widget.config(bg=widget.origBg) except: pass # not a flash label elif widgType == "OptionMenu": if external: widget.config(fg=fg) widget["menu"].config(fg=fg) # deal with generic groupers elif widgType in ["Frame", "LabelFrame", "PanedFrame", "Pane", "ajFrame"]: for child in widget.winfo_children(): gui.SET_WIDGET_FG(child, fg, external) # deal with specific containers elif widgType == "LabelBox": try: if not widget.isValidation: gui.SET_WIDGET_FG(widget.theLabel, fg, external) except Exception as e: gui.SET_WIDGET_FG(widget.theLabel, fg, external) gui.SET_WIDGET_FG(widget.theWidget, fg, external) elif widgType == "ButtonBox": gui.SET_WIDGET_FG(widget.theWidget, fg, external) gui.SET_WIDGET_FG(widget.theButton, fg, external) elif widgType == "WidgetBox": for child in widget.theWidgets: gui.SET_WIDGET_FG(child, fg, external) elif widgType == "ListBoxContainer": if external: gui.SET_WIDGET_FG(widget.lb, fg, external) # skip these widgets elif widgType in ["PieChart", "MicroBitSimulator", "Scrollbar"]: pass # always try these widgets else: try: widget.config(fg=fg) except Exception as e: pass
def SHOW_PATHS(
)
returns a printable string containing path to libraries, etc
@staticmethod def SHOW_PATHS(): """ returns a printable string containing path to libraries, etc """ pathString = \ "File Name: " + (gui.exe_file if gui.exe_file is not None else "") \ + "\nFile Location: " + (gui.exe_path if gui.exe_path is not None else "") \ + "\nLib Location: " + (gui.lib_path if gui.lib_path is not None else "") return pathString
def SHOW_VERSION(
)
returns a printable string containing version information
@staticmethod def SHOW_VERSION(): """ returns a printable string containing version information """ verString = \ "appJar: " + str(__version__) \ + "\nPython: " + str(sys.version_info[0]) \ + "." + str(sys.version_info[1]) + "." + str(sys.version_info[2]) \ + "\nTCL: " + str(TclVersion) \ + ", TK: " + str(TkVersion) \ + "\nPlatform: " + str(platform()) \ + "\npid: " + str(os.getpid()) \ + "\nlocale: " + str(__locale__) return verString
def SPLIT_GEOM(
geom)
returns 2 lists made from the geom string :param geom: the geom string to parse :returns: a tuple containing a width/heiht tuple & a x/y position tuple
@staticmethod def SPLIT_GEOM(geom): """ returns 2 lists made from the geom string :param geom: the geom string to parse :returns: a tuple containing a width/heiht tuple & a x/y position tuple """ geom = geom.lower().split("x") width = int(float(geom[0])) height = int(float(geom[1].split("+")[0])) try: x = int(float(geom[1].split("+")[1])) y = int(float(geom[1].split("+")[2])) except IndexError: x = y = -1 return (width, height), (x, y)
def TINT(
widget, colour)
@staticmethod def TINT(widget, colour): col = [] for a, b in enumerate(widget.winfo_rgb(colour)): t = int(min(max(0, b / 256 + (255 - b / 256) * .3), 255)) t = str(hex(t))[2:] if len(t) == 1: t = '0' + t elif len(t) == 0: t = '00' col.append(t) if int(col[0], 16) > 210 and int(col[1], 16) > 210 and int(col[2], 16) > 210: if gui.GET_PLATFORM() == gui.LINUX: return "#c3c3c3" else: return "systemHighlight" else: return "#" + "".join(col)
def addAppJarMenu(
self)
def addAppJarMenu(self): if self.platform == self.MAC: self.addMenuItem("MAC_APP", "About appJar", self.appJarAbout) self.addMenuWindow() self.addMenuHelp(self.appJarHelp) elif self.platform == self.WINDOWS: self.addMenuSeparator('WIN_SYS') self.addMenuItem("WIN_SYS", "About appJar", self.appJarAbout) self.addMenuItem("WIN_SYS", "appJar Help", self.appJarHelp)
def addAutoEntry(
self, title, words, row=None, column=0, colspan=0, rowspan=0)
def addAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="auto", words=words)
def addButton(
self, title, func, row=None, column=0, colspan=0, rowspan=0)
adds a button with the title as its text
def addButton(self, title, func, row=None, column=0, colspan=0, rowspan=0): ''' adds a button with the title as its text ''' but = self._buildButton(title, func, self.getContainer()) self._positionWidget(but, row, column, colspan, rowspan, None) return but
def addButtons(
self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False)
adds a 1D/2D list of buttons
def addButtons(self, names, funcs, row=None, column=0, colspan=0, rowspan=0, fill=False): ''' adds a 1D/2D list of buttons ''' if not isinstance(names, list): raise Exception( "Invalid button: " + names + ". It must be a list of buttons.") singleFunc = self._checkFunc(names, funcs) frame = self._makeWidgetBox()(self.getContainer()) if not self.ttk: frame.config(background=self._getContainerBg()) # make them into a 2D array, if not already if not isinstance(names[0], list): names = [names] # won't be used if single func if funcs is not None: funcs = [funcs] sticky = None if fill: sticky=E+W for bRow in range(len(names)): for i in range(len(names[bRow])): t = names[bRow][i] if funcs is None: tempFunc = None elif singleFunc is None: tempFunc = funcs[bRow][i] else: tempFunc = singleFunc but = self._buildButton(t, tempFunc, frame) but.grid(row=bRow, column=i, sticky=sticky) Grid.columnconfigure(frame, i, weight=1) Grid.rowconfigure(frame, bRow, weight=1) frame.theWidgets.append(but) self._positionWidget(frame, row, column, colspan, rowspan) self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)
def addCanvas(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds a canvas at the specified position
def addCanvas(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a canvas at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.Canvas, title) canvas = Canvas(self.getContainer()) canvas.config(bd=0, highlightthickness=0) canvas.imageStore = [] self._positionWidget(canvas, row, column, colspan, rowspan, "news") self.widgetManager.add(WIDGET_NAMES.Canvas, title, canvas) return canvas
def addCanvasCircle(
self, title, x, y, diameter, **kwargs)
adds a circle to the specified canvas
def addCanvasCircle(self, title, x, y, diameter, **kwargs): ''' adds a circle to the specified canvas ''' return self.addCanvasOval(title, x, y, diameter, diameter, **kwargs)
def addCanvasImage(
self, title, x, y, image=<function gui.image at 0x104631d08>, **kwargs)
adds an image to the specified canvas
def addCanvasImage(self, title, x, y, image=image, **kwargs): ''' adds an image to the specified canvas ''' canv = self.widgetManager.get(WIDGET_NAMES.Canvas, title) if isinstance(image, UNIVERSAL_STRING): image = self._getImage(image) canv.imageStore.append(image) return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_image(x, y, image=image, **kwargs)
def addCanvasLine(
self, title, x, y, x2, y2, **kwargs)
adds a line to the specified canvas
def addCanvasLine(self, title, x, y, x2, y2, **kwargs): ''' adds a line to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_line(x, y, x2, y2, **kwargs)
def addCanvasOval(
self, title, x, y, xDiam, yDiam, **kwargs)
adds a oval to the specified canvas
def addCanvasOval(self, title, x, y, xDiam, yDiam, **kwargs): ''' adds a oval to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_oval(x, y, x+xDiam, y+yDiam, **kwargs)
def addCanvasRectangle(
self, title, x, y, w, h, **kwargs)
adds a rectangle to the specified canvas
def addCanvasRectangle(self, title, x, y, w, h, **kwargs): ''' adds a rectangle to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_rectangle(x, y, x+w, y+h, **kwargs)
def addCanvasText(
self, title, x, y, text=None, **kwargs)
adds text to the specified canvas
def addCanvasText(self, title, x, y, text=None, **kwargs): ''' adds text to the specified canvas ''' return self.widgetManager.get(WIDGET_NAMES.Canvas, title).create_text(x, y, text=text, **kwargs)
def addCheckBox(
self, title, row=None, column=0, colspan=0, rowspan=0, name=None)
adds a new check box, at the specified position
def addCheckBox(self, title, row=None, column=0, colspan=0, rowspan=0, name=None): ''' adds a new check box, at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.CheckBox, title) var = IntVar(self.topLevel) if name is None: name = title if not self.ttkFlag: cb = Checkbutton(self.getContainer(), text=name, variable=var) cb.config( font=self._getContainerProperty('labelFont'), background=self._getContainerBg(), activebackground=self._getContainerBg(), anchor=W) else: cb = ttk.Checkbutton(self.getContainer(), text=name, variable=var) cb.DEFAULT_TEXT = name cb.bind("<Button-1>", self._grabFocus) self.widgetManager.add(WIDGET_NAMES.CheckBox, title, cb) self.widgetManager.add(WIDGET_NAMES.CheckBox, title, var, group=WidgetManager.VARS) self._positionWidget(cb, row, column, colspan, rowspan, EW) return cb
def addDatePicker(
self, name, row=None, column=0, colspan=0, rowspan=0)
adds a date picker at the specified position
def addDatePicker(self, name, row=None, column=0, colspan=0, rowspan=0): ''' adds a date picker at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.DatePicker, name) # initial DatePicker has these dates days = range(1, 32) self.MONTH_NAMES = calendar.month_name[1:] years = range(1970, 2021) # create a frame, and add the widgets frame = self.startFrame(name, row, column, colspan, rowspan) self.setExpand("none") self.addLabel(name + "_DP_DayLabel", "Day:", 0, 0) self.setLabelAlign(name + "_DP_DayLabel", "w") self.addOptionBox(name + "_DP_DayOptionBox", days, 0, 1) self.addLabel(name + "_DP_MonthLabel", "Month:", 1, 0) self.setLabelAlign(name + "_DP_MonthLabel", "w") self.addOptionBox(name + "_DP_MonthOptionBox", self.MONTH_NAMES, 1, 1) self.addLabel(name + "_DP_YearLabel", "Year:", 2, 0) self.setLabelAlign(name + "_DP_YearLabel", "w") self.addOptionBox(name + "_DP_YearOptionBox", years, 2, 1) self.setOptionBoxChangeFunction( name + "_DP_MonthOptionBox", self._updateDatePickerDays) self.setOptionBoxChangeFunction( name + "_DP_YearOptionBox", self._updateDatePickerDays) self.stopFrame() frame.isContainer = False self.widgetManager.add(WIDGET_NAMES.DatePicker, name, frame)
def addDbGrid(
self, title, db, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False)
DEPRECATED - adds a new table widget, with the specified database and table
def addDbGrid(self, title, db, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False): ''' DEPRECATED - adds a new table widget, with the specified database and table ''' gui.warn("Deprecated - grids renamed to tables") return self.addDbTable(title, db, table, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu)
def addDbOptionBox(
self, title, db, row=None, column=0, colspan=0, rowspan=0, **kwargs)
adds an option box, with a list of tables form the specified database
def addDbOptionBox(self, title, db, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds an option box, with a list of tables form the specified database ''' data = self._getDbTables(db) opt = self.option(title, data, row, column, colspan, rowspan, **kwargs) opt.db = db return opt
def addDbTable(
self, title, value, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False, border='solid', **kwargs)
creates a new Table, displaying the specified database & table
def addDbTable(self, title, value, table, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False, border="solid", **kwargs): ''' creates a new Table, displaying the specified database & table ''' horiz=kwargs.pop('horizontal', True) self._importSqlite3() if not sqlite3: self.error("Unable to load DB data - can't load sqlite3") return with sqlite3.connect(value) as conn: cursor = conn.cursor() dataQuery = 'SELECT * from ' + table # select all data cursor.execute(dataQuery) grid = self.addTable(title, cursor, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu, border=border, horizontal=horiz ) grid.db = value grid.dbTable = table return grid
def addDirectoryEntry(
self, title, row=None, column=0, colspan=0, rowspan=0)
def addDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="directory")
def addDualMeter(
self, name, row=None, column=0, colspan=0, rowspan=0)
def addDualMeter(self, name, row=None, column=0, colspan=0, rowspan=0): return self._addMeter(name, "DUAL", row, column, colspan, rowspan)
def addEmptyLabel(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds an empty label
def addEmptyLabel(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an empty label ''' return self.addLabel(title=title, text='', row=row, column=column, colspan=colspan, rowspan=rowspan)
def addEmptyMessage(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds an empty message box
def addEmptyMessage(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an empty message box ''' return self.addMessage(title, "", row, column, colspan, rowspan)
def addEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False)
adds an entry box for capturing text
def addEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False): ''' adds an entry box for capturing text ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="standard")
def addFileEntry(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds an entry box with a button, that pops-up a file dialog
def addFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box with a button, that pops-up a file dialog ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="file")
def addFlashLabel(
self, title, text=None, row=None, column=0, colspan=0, rowspan=0)
adds a label with flashing text
def addFlashLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a label with flashing text ''' lab = self.addLabel(title, text, row, column, colspan, rowspan) self.widgetManager.log(WIDGET_NAMES.FlashLabel, lab) self.doFlash = True return lab
def addGoogleMap(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds a GoogleMap widget at the specified position
def addGoogleMap(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a GoogleMap widget at the specified position ''' self._loadURL() self._loadTooltip() if urlencode is False: raise Exception("Unable to load GoogleMaps - urlencode library not available") self.widgetManager.verify(WIDGET_NAMES.Map, title) gMap = GoogleMap(self.getContainer(), self, useTtk = self.ttkFlag, font=self._getContainerProperty('labelFont')) self._positionWidget(gMap, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Map, title, gMap) return gMap
def addGrid(
self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False)
DEPRECATED - adds a new grid widget with the specified data
def addGrid(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False): ''' DEPRECATED - adds a new grid widget with the specified data ''' gui.warn("Deprecated - grids renamed to tables") return self.addTable(title, data, row, column, colspan, rowspan, action, addRow, actionHeading, actionButton, addButton, showMenu)
def addGridColumn(
self, title, columnNumber, data)
DEPRECATED - adds a column of data to the specified grid
def addGridColumn(self, title, columnNumber, data): ''' DEPRECATED - adds a column of data to the specified grid ''' return self.addTableColumn(title, columnNumber, data)
def addGridRow(
self, title, data)
DEPRECATED - adds a row of data to the specified grid
def addGridRow(self, title, data): ''' DEPRECATED - adds a row of data to the specified grid ''' return self.addTableRow(title, data)
def addGridRows(
self, title, data)
DEPRECATED - adds new rows of data to the specified grid
def addGridRows(self, title, data): ''' DEPRECATED - adds new rows of data to the specified grid ''' return self.addTableRows(title, data)
def addGrip(
self, row=None, column=0, colspan=0, rowspan=0)
adds a grip, for dragging the GUI around
def addGrip(self, row=None, column=0, colspan=0, rowspan=0): ''' adds a grip, for dragging the GUI around ''' grip = self._makeGrip()(self.getContainer()) self._positionWidget(grip, row, column, colspan, rowspan) self._addTooltip(grip, "Drag here to move", True) return grip
def addHorizontalSeparator(
self, row=None, column=0, colspan=0, rowspan=0, colour=None)
def addHorizontalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None): return self._addSeparator("horizontal", row, column, colspan, rowspan, colour)
def addIcon(
self, name, iconName, row=None, column=0, colspan=0, rowspan=0, compound=None)
adds one of the built-in icons at the specified position
def addIcon(self, name, iconName, row=None, column=0, colspan=0, rowspan=0, compound=None): ''' adds one of the built-in icons at the specified position ''' icon = os.path.join(self.icon_path, iconName.lower()+".png") with PauseLogger(): return self.addImage(name, icon, row, column, colspan, rowspan, compound=compound)
def addIconButton(
self, title, func, iconName, row=None, column=0, colspan=0, rowspan=0, align=None)
adds a button displaying the specified icon
def addIconButton(self, title, func, iconName, row=None, column=0, colspan=0, rowspan=0, align=None): ''' adds a button displaying the specified icon ''' icon = os.path.join(self.icon_path, iconName.lower()+".png") with PauseLogger(): return self.addImageButton(title, func, icon, row, column, colspan, rowspan, align)
def addImage(
self, name, imageFile, row=None, column=0, colspan=0, rowspan=0, compound=None)
Adds an image at the specified position
def addImage(self, name, imageFile, row=None, column=0, colspan=0, rowspan=0, compound=None): ''' Adds an image at the specified position ''' self.widgetManager.verify(WIDGET_NAMES.Image, name) imgObj = self._getImage(imageFile) self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound) self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False return imgObj
def addImageButton(
self, title, func, imgFile, row=None, column=0, colspan=0, rowspan=0, align=None)
adds a button, displaying the specified image file
def addImageButton(self, title, func, imgFile, row=None, column=0, colspan=0, rowspan=0, align=None): ''' adds a button, displaying the specified image file ''' but = self._buildButton(title, func, self.getContainer()) self._positionWidget(but, row, column, colspan, rowspan, None) self.setButtonImage(title, imgFile, align) return but
def addImageData(
self, name, imageData, row=None, column=0, colspan=0, rowspan=0, fmt='gif', compound=None)
load image from base-64 encoded GIF use base64 module to convert binary data to base64
def addImageData(self, name, imageData, row=None, column=0, colspan=0, rowspan=0, fmt="gif", compound=None): ''' load image from base-64 encoded GIF use base64 module to convert binary data to base64 ''' self.widgetManager.verify(WIDGET_NAMES.Image, name) imgObj = self._getImageData(imageData, fmt) self._addImageObj(name, imgObj, row, column, colspan, rowspan, compound=compound) self.widgetManager.get(WIDGET_NAMES.Image, name).hasMouseOver = False return imgObj
def addLabel(
self, title, text=None, row=None, column=0, colspan=0, rowspan=0, selectable=False)
Add a label to the GUI. :param title: a unique identifier for the Label :param text: optional text for the Label :param row/column/colspan/rowspan: the row/column to position the label in & how many rows/columns to strecth across :raises ItemLookupError: raised if the title is not unique
def addLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0, selectable=False): """Add a label to the GUI. :param title: a unique identifier for the Label :param text: optional text for the Label :param row/column/colspan/rowspan: the row/column to position the label in & how many rows/columns to strecth across :raises ItemLookupError: raised if the title is not unique """ self.widgetManager.verify(WIDGET_NAMES.Label, title) if text is None: gui.trace("Not specifying text for labels (%s) now uses the title for the text. If you want an empty label, pass an empty string ''", title) text = title if not selectable: if not self.ttkFlag: lab = Label(self.getContainer(), text=text) lab.config(justify=LEFT, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) lab.origBg = self._getContainerBg() else: lab = ttk.Label(self.getContainer(), text=text) else: lab = SelectableLabel(self.getContainer(), text=text) lab.config(justify=CENTER, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) lab.origBg = self._getContainerBg() lab.inContainer = False lab.DEFAULT_TEXT = text self.widgetManager.add(WIDGET_NAMES.Label, title, lab) self._positionWidget(lab, row, column, colspan, rowspan) return lab
def addLabelAutoEntry(
self, title, words, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)
def addLabelAutoEntry(self, title, words, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="auto", words=words)
def addLabelDirectoryEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
def addLabelDirectoryEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="directory")
def addLabelEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)
adds an entry box for capturing text, with the title as a label
def addLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): ''' adds an entry box for capturing text, with the title as a label ''' return self._entryMaker(title, row, column, colspan, rowspan, secret, label=label)
def addLabelFileEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
adds an entry box with a button, that pops-up a file dialog, with a label that displays the title
def addLabelFileEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box with a button, that pops-up a file dialog, with a label that displays the title ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="file")
def addLabelNumericEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)
def addLabelNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric")
def addLabelOpenEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
adds an entry box with a button, that pops-up a open dialog, with a label that displays the title
def addLabelOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box with a button, that pops-up a open dialog, with a label that displays the title ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="open")
def addLabelOptionBox(
self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)
Adds a new standard OptionBox, with a Label before it. Simply calls internal function _buildOptionBox, placing it in a LabelBox.
:param title: the key used to reference this OptionBox and text for the Label :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use
def addLabelOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs): """ Adds a new standard OptionBox, with a Label before it. Simply calls internal function _buildOptionBox, placing it in a LabelBox. :param title: the key used to reference this OptionBox and text for the Label :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use """ frame = self._getLabelBox(title, **kwargs) option = self._buildOptionBox(frame, title, options, disabled=disabled) self._packLabelBox(frame, option) self._positionWidget(frame, row, column, colspan, rowspan) return option
def addLabelSaveEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
adds an entry box with a button, that pops-up a save dialog, with a label that displays the title
def addLabelSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box with a button, that pops-up a save dialog, with a label that displays the title ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="save")
def addLabelScale(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
adds a slidable scale, with a label showing the title at the specified position
def addLabelScale(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds a slidable scale, with a label showing the title at the specified position ''' frame = self._getLabelBox(title, label=label) scale = self._buildScale(title, frame) self._packLabelBox(frame, scale) self._positionWidget(frame, row, column, colspan, rowspan) return scale
def addLabelSecretEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
adds an entry box for capturing text, where the text is displayed as stars, with the title as a label
def addLabelSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label)
def addLabelSpinBox(
self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs)
adds a spinbox, with the specified values, and a label displaying the title
def addLabelSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a spinbox, with the specified values, and a label displaying the title ''' frame = self._getLabelBox(title, **kwargs) spin = self._buildSpinBox(frame, title, values) self._packLabelBox(frame, spin) self._positionWidget(frame, row, column, colspan, rowspan) self.setSpinBoxPos(title, 0) return spin
def addLabelSpinBoxRange(
self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, label=True, **kwargs)
adds a spinbox, with a range of whole numbers, and a label displaying the title
def addLabelSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, label=True, **kwargs): ''' adds a spinbox, with a range of whole numbers, and a label displaying the title ''' vals = list(range(fromVal, toVal + 1)) spin = self.addLabelSpinBox(title, vals, row, column, colspan, rowspan, label=label) spin.isRange = True return spin
def addLabelTickOptionBox(
self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)
Adds a new TickOptionBox, with a Label before it Simply calls internal function _buildOptionBox, placing it in a LabelBox
:param title: the key used to reference this TickOptionBox, and text for the Label :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use
def addLabelTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs): """ Adds a new TickOptionBox, with a Label before it Simply calls internal function _buildOptionBox, placing it in a LabelBox :param title: the key used to reference this TickOptionBox, and text for the Label :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox (not the LabelBox) :raises ItemLookupError: if the title is already in use """ frame = self._getLabelBox(title, **kwargs) tick = self._buildOptionBox(frame, title, options, kind="ticks", disabled=disabled) self._packLabelBox(frame, tick) self._positionWidget(frame, row, column, colspan, rowspan) return tick
def addLabelValidationEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)
def addLabelValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=label, kind="validation")
def addLabels(
self, names, row=None, colspan=0, rowspan=0)
adds a set of labels, in the row, spannning specified columns
def addLabels(self, names, row=None, colspan=0, rowspan=0): ''' adds a set of labels, in the row, spannning specified columns ''' frame = self._makeWidgetBox()(self.getContainer()) if not self.ttkFlag: frame.config(background=self._getContainerBg()) for i in range(len(names)): self.widgetManager.verify(WIDGET_NAMES.Label, names[i]) if not self.ttkFlag: lab = Label(frame, text=names[i]) lab.config(font=self._getContainerProperty('labelFont'), justify=LEFT, background=self._getContainerBg()) else: lab = ttk.Label(frame, text=names[i]) lab.DEFAULT_TEXT = names[i] lab.inContainer = False self.widgetManager.add(WIDGET_NAMES.Label, names[i], lab) lab.grid(row=0, column=i) Grid.columnconfigure(frame, i, weight=1) Grid.rowconfigure(frame, 0, weight=1) frame.theWidgets.append(lab) self._positionWidget(frame, row, 0, colspan, rowspan) self.widgetManager.log(WIDGET_NAMES.FrameBox, frame)
def addLink(
self, title, func, row=None, column=0, colspan=0, rowspan=0)
adds a hyperlink to the specified function
def addLink(self, title, func, row=None, column=0, colspan=0, rowspan=0): ''' adds a hyperlink to the specified function ''' link = self._buildLink(title) if func is not None: myF = self.MAKE_FUNC(func, title) link.registerCallback(myF) self._positionWidget(link, row, column, colspan, rowspan) return link
def addListBox(
self, name, values=None, row=None, column=0, colspan=0, rowspan=0)
adds a list box, with the the specified list of values
def addListBox(self, name, values=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a list box, with the the specified list of values ''' self.widgetManager.verify(WIDGET_NAMES.ListBox, name) container = self.makeListBoxContainer()(self.getContainer()) vscrollbar = AutoScrollbar(container) hscrollbar = AutoScrollbar(container, orient=HORIZONTAL) container.lb = Listbox(container, yscrollcommand=vscrollbar.set, xscrollcommand=hscrollbar.set) vscrollbar.grid(row=0, column=1, sticky=N + S) hscrollbar.grid(row=1, column=0, sticky=E + W) container.lb.grid(row=0, column=0, sticky=N + S + E + W) container.grid_rowconfigure(0, weight=1) container.grid_columnconfigure(0, weight=1) vscrollbar.config(command=container.lb.yview) hscrollbar.config(command=container.lb.xview) container.lb.config(font=self._getContainerProperty('inputFont')) self.widgetManager.add(WIDGET_NAMES.ListBox, name, container.lb) container.lb.DEFAULT_TEXT="" if values is not None: container.lb.DEFAULT_TEXT='\n'.join(str(x) for x in values) for name in values: container.lb.insert(END, name) self._positionWidget(container, row, column, colspan, rowspan) return container.lb
def addListItem(
self, title, item, pos=None, select=True)
add the item to the end of the specified list box
def addListItem(self, title, item, pos=None, select=True): ''' add the item to the end of the specified list box ''' lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) # add it at the end if pos is None: pos = END lb.insert(pos, item) # show & select the newly added item if select: # clear any selection items = lb.curselection() if len(items) > 0: lb.selection_clear(items) self.selectListItemAtPos(title, lb.size() - 1)
def addListItems(
self, title, items, select=True)
adds the list of items to the specified list box
def addListItems(self, title, items, select=True): ''' adds the list of items to the specified list box ''' for i in items: self.addListItem(title, i, select=select)
def addMenu(
self, name, func, shortcut=None, underline=-1)
def addMenu(self, name, func, shortcut=None, underline=-1): self.addMenuItem(None, name, func=func, kind="topLevel", shortcut=shortcut, underline=underline)
def addMenuCheckBox(
self, menu, name, func=None, shortcut=None, underline=-1)
def addMenuCheckBox(self, menu, name, func=None, shortcut=None, underline=-1): self.addMenuItem(menu, name, func, "cb", shortcut, underline)
def addMenuEdit(
self, inMenuBar=False)
def addMenuEdit(self, inMenuBar=False): self._initMenu() self.copyAndPaste.inUse = True # in case we already made the menu - just return try: self.widgetManager.verify(WIDGET_NAMES.Menu, "EDIT") except: return editMenu = Menu(self.menuBar, tearoff=False) editMenu.bind("<FocusOut>", lambda e: editMenu.unpost()) if inMenuBar: self.menuBar.add_cascade(menu=editMenu, label='Edit ') self.widgetManager.add(WIDGET_NAMES.Menu, "EDIT", editMenu) if gui.GET_PLATFORM() == gui.MAC: shortcut = "Command-" else: shortcut = "Control-" eList = [ ('Cut', lambda e: self._copyAndPasteHelper("Cut"), "X", False), ('Copy', lambda e: self._copyAndPasteHelper("Copy"), "C", False), ('Paste', lambda e: self._copyAndPasteHelper("Paste"), "V", False), ('Select All', lambda e: self._copyAndPasteHelper("Select All"), "A", True if gui.GET_PLATFORM() == gui.MAC else False), ('Clear Clipboard', lambda e: self._copyAndPasteHelper("Clear Clipboard"), None, False) ] for (txt, cmd, sc, bind) in eList: acc = None if sc is None else shortcut + sc self.addMenuItem("EDIT", txt, cmd, shortcut=acc, createBinding=bind) # add a clear option self.addMenuSeparator("EDIT") self.addMenuItem("EDIT", "Clear All", lambda e: self._copyAndPasteHelper("Clear All")) self.addMenuSeparator("EDIT") self.addMenuItem("EDIT", 'Undo', lambda e: self._copyAndPasteHelper("Undo"), shortcut=shortcut + "Z", createBinding=False) self.addMenuItem("EDIT", 'Redo', lambda e: self._copyAndPasteHelper( "Redo"), shortcut=shortcut+"Shift-Z", createBinding=True) self.addMenuSeparator("EDIT") self.addMenuItem("EDIT", "Bold", lambda e: self._copyAndPasteHelper("BOLD"), shortcut=shortcut+"B") self.addMenuItem("EDIT", "Italic", lambda e: self._copyAndPasteHelper("ITALIC"), shortcut=shortcut+"I") self.addMenuItem("EDIT", "Underline", lambda e: self._copyAndPasteHelper("UNDERLINE"), shortcut=shortcut+"U") self.addMenuItem("EDIT", "Bold & Italic", lambda e: self._copyAndPasteHelper("BOLD_ITALIC"), shortcut=shortcut+"Shift-B") self.disableMenu("EDIT")
def addMenuHelp(
self, func)
def addMenuHelp(self, func): if self.platform == self.MAC: self._initMenu() helpMenu = Menu(self.menuBar, name='help') self.menuBar.add_cascade(menu=helpMenu, label='Help') u = self.MAKE_FUNC(func, "help") self.topLevel.createcommand('tk::mac::ShowHelp', u) self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_HELP", helpMenu) else: self.warn("The Help Menu is specific to Mac OSX")
def addMenuItem(
self, title, item, func=None, kind=None, shortcut=None, underline=-1, rb_id=None, createBinding=True)
def addMenuItem(self, title, item, func=None, kind=None, shortcut=None, underline=-1, rb_id=None, createBinding=True): # set the initial menubar self._initMenu() # get or create an initial menu if title is not None: try: theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) except: theMenu = self.createMenu(title) if theMenu is None: gui.warn('Unable to create menu: %s', title) return if underline > -1 and self.platform == self.MAC: gui.warn("Underlining menu items not available on MAC") if func is not None: func = self.MAKE_FUNC(func, item) acc = None if shortcut is not None: if kind == 'cb': f = lambda e: self._menuCheckButtonBind(title, item, func) binding = EventBinding(shortcut, f, self._getTopLevel(), menuBinding=True) else: binding = EventBinding(shortcut, func, self._getTopLevel(), menuBinding=True) try: self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding) if createBinding: binding.createBindings() acc = binding.displayName except ItemLookupError: raise ItemLookupError('Unable to bind menu ' + item + ' to ' + binding.displayName + ' - binding already exists') # now, let's create the actual menu item if item == "-" or kind == "separator": theMenu.add_separator() elif kind == "topLevel" or title is None: if self.platform == self.MAC: self.warn("Unable to make topLevel menus (%s) on Mac", item) else: self.menuBar.add_command( label=item, command=func, accelerator=acc, underline=underline) elif kind == "rb": varName = title + "rb" + item newRb = False if (varName in self.widgetManager.group(WIDGET_NAMES.Menu, group=WidgetManager.VARS)): var = self.widgetManager.get(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS) else: newRb = True var = StringVar(self.topLevel) self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS) theMenu.add_radiobutton(label=rb_id, command=func, variable=var, value=rb_id, accelerator=acc, underline=underline) if newRb: self.setMenuRadioButton(title, item, rb_id) elif kind == "cb": varName = title + "cb" + item self.widgetManager.verify(WIDGET_NAMES.Menu, varName, group=WidgetManager.VARS) var = BooleanVar(self.topLevel) var.set(False) self.widgetManager.add(WIDGET_NAMES.Menu, varName, var, group=WidgetManager.VARS) theMenu.add_checkbutton(label=item, command=func, variable=var, onvalue=True, offvalue=False, accelerator=acc, underline=underline) elif kind == "sub": self.widgetManager.verify(WIDGET_NAMES.Menu, item) subMenu = Menu(theMenu, tearoff=False) self.widgetManager.add(WIDGET_NAMES.Menu, item, subMenu) theMenu.add_cascade(label=item, menu=subMenu) else: theMenu.add_command(label=item, command=func, accelerator=acc, underline=underline)
def addMenuList(
self, menuName, names, funcs)
def addMenuList(self, menuName, names, funcs): # deal with a dict_keys object - messy!!!! if not isinstance(names, list): names = list(names) # append some Nones, if it's a list and contains separators if funcs is not None: if not callable(funcs): seps = names.count("-") for i in range(seps): funcs.append(None) singleFunc = self._checkFunc(names, funcs) # add menu items for t in names: if funcs is None: u = None elif singleFunc is not None: u = singleFunc else: u = funcs.pop(0) self.addMenuItem(menuName, t, u)
def addMenuPreferences(
self, func)
def addMenuPreferences(self, func): if self.platform == self.MAC: self._initMenu() u = self.MAKE_FUNC(func, "preferences") self.topLevel.createcommand('tk::mac::ShowPreferences', u) else: self.warn("The Preferences Menu is specific to Mac OSX")
def addMenuRadioButton(
self, menu, name, value, func=None, shortcut=None, underline=-1)
def addMenuRadioButton(self, menu, name, value, func=None, shortcut=None, underline=-1): self.addMenuItem(menu, name, func, "rb", shortcut, underline, value)
def addMenuSeparator(
self, menu)
def addMenuSeparator(self, menu): self.addMenuItem(menu, "-")
def addMenuWindow(
self)
def addMenuWindow(self): if self.platform == self.MAC: self._initMenu() windowMenu = Menu(self.menuBar, name='window') self.menuBar.add_cascade(menu=windowMenu, label='Window') self.widgetManager.add(WIDGET_NAMES.Menu, "MAC_WIN", windowMenu) else: self.warn("The Window Menu is specific to Mac OSX")
def addMessage(
self, title, text=None, row=None, column=0, colspan=0, rowspan=0)
adds a message box, to display text across multiple lines
def addMessage(self, title, text=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a message box, to display text across multiple lines ''' self.widgetManager.verify(WIDGET_NAMES.Message, title) if text is None: text = title gui.trace("Not specifying text for messages (%s) now uses the title for the text. If you want an empty message, pass an empty string ''", title) mess = Message(self.getContainer()) mess.config(text=text) mess.config(font=self._getContainerProperty('labelFont')) mess.config(justify=LEFT, background=self._getContainerBg()) mess.DEFAULT_TEXT = text if self.platform in [self.MAC, self.LINUX]: mess.config(highlightbackground=self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.Message, title, mess) self._positionWidget(mess, row, column, colspan, rowspan) mess.bind("<Configure>", lambda e: mess.config(width=e.width-10)) return mess
def addMeter(
self, name, row=None, column=0, colspan=0, rowspan=0)
def addMeter(self, name, row=None, column=0, colspan=0, rowspan=0): return self._addMeter(name, "METER", row, column, colspan, rowspan)
def addMicroBit(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds a simple microbit widget used with permission from Ben Goodwin
def addMicroBit(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a simple microbit widget used with permission from Ben Goodwin ''' self.widgetManager.verify(WIDGET_NAMES.MicroBit, title) mb = MicroBitSimulator(self.getContainer()) self._positionWidget(mb, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.MicroBit, title, mb) return mb
def addNamedButton(
self, name, title, func, row=None, column=0, colspan=0, rowspan=0)
adds a button, displaying the name as its text
def addNamedButton(self, name, title, func, row=None, column=0, colspan=0, rowspan=0): ''' adds a button, displaying the name as its text ''' but = self._buildButton(title, func, self.getContainer(), name) self._positionWidget(but, row, column, colspan, rowspan, None) return but
def addNamedCheckBox(
self, name, title, row=None, column=0, colspan=0, rowspan=0)
adds a new check box, at the specified position, with the name as the text
def addNamedCheckBox(self, name, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a new check box, at the specified position, with the name as the text ''' return self.addCheckBox(title, row, column, colspan, rowspan, name)
def addNumericEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False)
def addNumericEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False): return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=False, kind="numeric")
def addNumericLabelEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True)
def addNumericLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False, label=True): return self._entryMaker(title, row, column, colspan, rowspan, secret=secret, label=label, kind="numeric")
def addOpenEntry(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds an entry box with a button, that pops-up a open dialog
def addOpenEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box with a button, that pops-up a open dialog ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="open")
def addOptionBox(
self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)
Adds a new standard OptionBox. Simply calls internal function _buildOptionBox.
:param title: the key used to reference this OptionBox :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox :raises ItemLookupError: if the title is already in use
def addOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs): """ Adds a new standard OptionBox. Simply calls internal function _buildOptionBox. :param title: the key used to reference this OptionBox :param options: a list of values to put in the OptionBox, can be len 0 :returns: the created OptionBox :raises ItemLookupError: if the title is already in use """ option = self._buildOptionBox(self.getContainer(), title, options, disabled=disabled) self._positionWidget(option, row, column, colspan, rowspan) return option
def addPieChart(
self, name, fracs, row=None, column=0, colspan=0, rowspan=0)
def addPieChart(self, name, fracs, row=None, column=0, colspan=0, rowspan=0): self.widgetManager.verify(WIDGET_NAMES.PieChart, name) self._loadTooltip() pie = PieChart(self.getContainer(), fracs, self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.PieChart, name, pie) self._positionWidget(pie, row, column, colspan, rowspan, sticky=None) return pie
def addPlot(
self, title, t, s, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False)
adds a MatPlotLib, with t/s plotted
def addPlot(self, title, t, s, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False): ''' adds a MatPlotLib, with t/s plotted ''' canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav) axes = fig.add_subplot(111) axes.plot(t,s) canvas.axes = axes return axes
def addPlotFig(
self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False)
def addPlotFig(self, title, row=None, column=0, colspan=0, rowspan=0, width=None, height=None, showNav=False): canvas, fig = self._addPlotFig(title, row, column, colspan, rowspan, width, height, showNav) return fig
def addProperties(
self, title, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs)
adds a new properties widget, displaying the dictionary of booleans as tick boxes
def addProperties(self, title, values=None, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a new properties widget, displaying the dictionary of booleans as tick boxes ''' self.widgetManager.verify(WIDGET_NAMES.Properties, title) haveTitle = True if self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame: self.containerStack[-1]['sticky'] = "ew" haveTitle = False props = Properties(self.getContainer(), title, values, haveTitle, font=self._getContainerProperty('labelFont'), background=self._getContainerBg()) self._positionWidget(props, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Properties, title, props) return props
def addRadioButton(
self, title, name, row=None, column=0, colspan=0, rowspan=0)
adds a radio button, to thr group 'title' with the text 'name'
def addRadioButton(self, title, name, row=None, column=0, colspan=0, rowspan=0): ''' adds a radio button, to thr group 'title' with the text 'name' ''' ident = title + "-" + name self.widgetManager.verify(WIDGET_NAMES.RadioButton, ident) var = None newRb = False # title - is the grouper # so, if we already have an entry in n_rbVars - get it if (title in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS)): var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS) else: # if this is a new grouper - set it all up var = StringVar(self.topLevel) self.widgetManager.add(WIDGET_NAMES.RadioButton, title, var, group=WidgetManager.VARS) newRb = True # finally, create the actual RadioButton if not self.ttkFlag: rb = Radiobutton(self.getContainer(), text=name, variable=var, value=name) rb.config(anchor=W, background=self._getContainerBg(), indicatoron=1, activebackground=self._getContainerBg(), font=self._getContainerProperty('labelFont') ) else: rb = ttk.Radiobutton(self.getContainer(), text=name, variable=var, value=name) rb.bind("<Button-1>", self._grabFocus) rb.DEFAULT_TEXT = name self.widgetManager.add(WIDGET_NAMES.RadioButton, ident, rb) #rb.bind("<Tab>", self._focusNextWindow) #rb.bind("<Shift-Tab>", self._focusLastWindow) # and select it, if it's the first item in the list if newRb: rb.select() if not self.ttkFlag else rb.invoke() var.startVal = name # so we can reset it... self._positionWidget(rb, row, column, colspan, rowspan, EW) return rb
def addSaveEntry(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds an entry box with a button, that pops-up a save dialog
def addSaveEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box with a button, that pops-up a save dialog ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="save")
def addScale(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds a slidable scale at the specified position
def addScale(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a slidable scale at the specified position ''' scale = self._buildScale(title, self.getContainer()) self._positionWidget(scale, row, column, colspan, rowspan) return scale
def addScrolledTextArea(
self, title, row=None, column=0, colspan=0, rowspan=0, text=None)
Adds a Scrollable TextArea with the specified title Simply calls internal _buildTextArea functio, specifying a ScrollabelTextArea before positioning the widget
:param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use
def addScrolledTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None): """ Adds a Scrollable TextArea with the specified title Simply calls internal _buildTextArea functio, specifying a ScrollabelTextArea before positioning the widget :param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use """ txt = self._buildTextArea(title, self.getContainer(), True) self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W) if text is not None: self.setTextArea(title, text, callFunction=False) return txt
def addSecretEntry(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds an entry box for capturing text, where the text is displayed as stars
def addSecretEntry(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds an entry box for capturing text, where the text is displayed as stars ''' return self._entryMaker(title, row, column, colspan, rowspan, True)
def addSecretLabelEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, label=True)
adds an entry box for capturing text, where the text is displayed as stars, with the title as a label
def addSecretLabelEntry(self, title, row=None, column=0, colspan=0, rowspan=0, label=True): ''' adds an entry box for capturing text, where the text is displayed as stars, with the title as a label ''' return self._entryMaker(title, row, column, colspan, rowspan, secret=True, label=label)
def addSelectableLabel(
self, title, text=None, row=None, column=0, colspan=0, rowspan=0)
adds a label with selectable text
def addSelectableLabel(self, title, text=None, row=None, column=0, colspan=0, rowspan=0): ''' adds a label with selectable text ''' return self.addLabel(title, text, row, column, colspan, rowspan, selectable=True)
def addSpinBox(
self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs)
adds a spinbox, with the specified values
def addSpinBox(self, title, values, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a spinbox, with the specified values ''' return self._addSpinBox(title, values, row, column, colspan, rowspan)
def addSpinBoxRange(
self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, **kwargs)
adds a spinbox, with a range of whole numbers
def addSpinBoxRange(self, title, fromVal, toVal, row=None, column=0, colspan=0, rowspan=0, **kwargs): ''' adds a spinbox, with a range of whole numbers ''' vals = list(range(fromVal, toVal + 1)) spin = self._addSpinBox(title, vals, row, column, colspan, rowspan) spin.isRange = True return spin
def addSplitMeter(
self, name, row=None, column=0, colspan=0, rowspan=0)
def addSplitMeter(self, name, row=None, column=0, colspan=0, rowspan=0): return self._addMeter(name, "SPLIT", row, column, colspan, rowspan)
def addStatusbar(
self, header='', fields=1, side=None)
def addStatusbar(self, header="", fields=1, side=None): if not self.hasStatus: class Statusbar(Frame, object): def __init__(self, master, **kwargs): super(Statusbar, self).__init__(master, **kwargs) self.hasStatus = True self.header = header self.statusFrame = Statusbar(self.appWindow) self.statusFrame.config(bd=1, relief=SUNKEN) self.statusFrame.pack(side=BOTTOM, fill=X, anchor=S) self._statusFields = [] for i in range(fields): self._statusFields.append(Label(self.statusFrame)) self._statusFields[i].config( bd=1, relief=SUNKEN, anchor=W, font=self._statusFont, width=10) self._addTooltip(self._statusFields[i], "Status bar", True) if side == "LEFT": self._statusFields[i].pack(side=LEFT) elif side == "RIGHT": self._statusFields[i].pack(side=RIGHT) else: self._statusFields[i].pack(side=LEFT, expand=1, fill=BOTH) else: self.error("Statusbar already exists - ignoring")
def addSubMenu(
self, menu, subMenu)
def addSubMenu(self, menu, subMenu): self.addMenuItem(menu, subMenu, func=None, kind="sub")
def addTable(
self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading='Action', actionButton='Press', addButton='Add', showMenu=False, border='solid', **kwargs)
creates a new table, displaying the specified data
def addTable(self, title, data, row=None, column=0, colspan=0, rowspan=0, action=None, addRow=None, actionHeading="Action", actionButton="Press", addButton="Add", showMenu=False, border="solid", **kwargs): ''' creates a new table, displaying the specified data ''' self.widgetManager.verify(WIDGET_NAMES.Table, title) wrap=kwargs.pop('wrap', 250) horiz=kwargs.pop('horizontal', True) if not self.ttkFlag: grid = SimpleTable(self.getContainer(), title, data, action, addRow, actionHeading, actionButton, addButton, showMenu, buttonFont=self._getContainerProperty('buttonFont'), font=self.tableFont, background=self._getContainerBg(), queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz ) else: grid = SimpleTable(self.getContainer(), title, data, action, addRow, actionHeading, actionButton, addButton, showMenu, buttonFont=self._getContainerProperty('buttonFont'), queueFunction=self.queueFunction, border=border, wrap=wrap, horizontal=horiz ) self._positionWidget(grid, row, column, colspan, rowspan, N+E+S+W) self.widgetManager.add(WIDGET_NAMES.Table, title, grid) return grid
def addTableColumn(
self, title, columnNumber, data)
adds a new column of data, in the specified position, to the specified table
def addTableColumn(self, title, columnNumber, data): ''' adds a new column of data, in the specified position, to the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.addColumn(columnNumber, data)
def addTableRow(
self, title, data)
adds a new row of data to the specified table
def addTableRow(self, title, data): ''' adds a new row of data to the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.addRow(data)
def addTableRows(
self, title, data)
adds multiple rows of data to the specified table
def addTableRows(self, title, data): ''' adds multiple rows of data to the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.addRows(data, scroll=True)
def addTextArea(
self, title, row=None, column=0, colspan=0, rowspan=0, text=None)
Adds a TextArea with the specified title Simply calls internal _buildTextArea function before positioning the widget
:param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use
def addTextArea(self, title, row=None, column=0, colspan=0, rowspan=0, text=None): """ Adds a TextArea with the specified title Simply calls internal _buildTextArea function before positioning the widget :param title: the key used to reference this TextArea :returns: the created TextArea :raises ItemLookupError: if the title is already in use """ txt = self._buildTextArea(title, self.getContainer()) self._positionWidget(txt, row, column, colspan, rowspan, N+E+S+W) if text is not None: self.setTextArea(title, text, callFunction=False) return txt
def addTickOptionBox(
self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled='-', **kwargs)
Adds a new TickOptionBox. Simply calls internal function _buildOptionBox.
:param title: the key used to reference this TickOptionBox :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox :raises ItemLookupError: if the title is already in use
def addTickOptionBox(self, title, options, row=None, column=0, colspan=0, rowspan=0, disabled="-", **kwargs): """ Adds a new TickOptionBox. Simply calls internal function _buildOptionBox. :param title: the key used to reference this TickOptionBox :param options: a list of values to put in the TickOptionBox, can be len 0 :returns: the created TickOptionBox :raises ItemLookupError: if the title is already in use """ tick = self._buildOptionBox(self.getContainer(), title, options, kind="ticks", disabled=disabled) self._positionWidget(tick, row, column, colspan, rowspan) return tick
def addToolbar(
self, names, funcs, findIcon=False, **kwargs)
def addToolbar(self, names, funcs, findIcon=False, **kwargs): # hide the toolbarMin bar if self.tb.toolbarMin is not None: self.tb.toolbarMin.pack_forget() # make sure the toolbar is showing try: self.tb.pack_info() except: self.tb.location = self.containerStack[0]['container'] self.tb.pack(before=self.tb.location, side=TOP, fill=X) if not self.tb.inUse: self.tb.inUse = True image = None singleFunc = self._checkFunc(names, funcs) if not isinstance(names, list): names = [names] for i in range(len(names)): t = names[i] if (t in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception( "Invalid toolbar button name: " + t + " already exists") if findIcon: # turn off warnings about PNGs with PauseLogger(): imgFile = os.path.join(self.icon_path, t.lower() + ".png") try: image = self._getImage(imgFile) except Exception as e: image = None if not self.ttkFlag: but = Button(self.tb) but.config(relief=FLAT, font=self._buttonFont) if gui.GET_PLATFORM() == gui.MAC and self.tb.BG_COLOR is not None: but.config(highlightbackground=self.tb.BG_COLOR) else: but = ttk.Button(self.tb) self.widgetManager.add(WIDGET_NAMES.Toolbar, t, but) if singleFunc is not None: u = self.MAKE_FUNC(singleFunc, t) else: u = self.MAKE_FUNC(funcs[i], t) but.config(command=u) if image is not None: # works on Mac & Windows :) but.config(image=image) but.image = image if not self.ttkFlag: but.config(justify=LEFT, compound=TOP) else: but.config(style="Toolbar.TButton") else: but.config(text=t) but.pack(side=LEFT, padx=2, pady=2) but.tt_var = self._addTooltip(but, t.title(), True) but.DEFAULT_TEXT=t
def addToolbarButton(
self, name, func, findIcon=False)
def addToolbarButton(self, name, func, findIcon=False): self.addToolbar([name], func, findIcon)
def addTrashBin(
self, title, row=None, column=0, colspan=0, rowspan=0)
NOT IN USE - adds a trashbin, for discarding dragged items
def addTrashBin(self, title, row=None, column=0, colspan=0, rowspan=0): ''' NOT IN USE - adds a trashbin, for discarding dragged items ''' trash = TrashBin(self.getContainer()) self._positionWidget(trash, row, column, colspan, rowspan) return trash
def addTree(
self, title, data, row=None, column=0, colspan=0, rowspan=0)
adds a navigatable tree, displaying the specified xml text
def addTree(self, title, data, row=None, column=0, colspan=0, rowspan=0): ''' adds a navigatable tree, displaying the specified xml text ''' self.widgetManager.verify(WIDGET_NAMES.Tree, title) self._importAjtree() if parseString is False: self.warn("Unable to parse xml files. .addTree() not available") return if isinstance(data, UNIVERSAL_STRING): data = parseString(data) else: pass # assume xml object return self._buildTree(title, data, row, column, colspan, rowspan)
def addTreeChild(
self, title, data)
def addTreeChild(self, title, data): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) if isinstance(data, UNIVERSAL_STRING): data = parseString(data) treeData = self._makeAjTreeData()(data) tree.addChild(treeData)
def addTurtle(
self, title, row=None, column=0, colspan=0, rowspan=0)
adds a turtle widget at the specified position
def addTurtle(self, title, row=None, column=0, colspan=0, rowspan=0): ''' adds a turtle widget at the specified position ''' self._loadTurtle() if turtle is False: raise Exception("Unable to load turtle") self.widgetManager.verify(WIDGET_NAMES.Turtle, title) canvas = Canvas(self.getContainer()) canvas.screen = turtle.TurtleScreen(canvas) self._positionWidget(canvas, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Turtle, title, canvas) canvas.turtle = turtle.RawTurtle(canvas.screen) return canvas.turtle
def addValidationEntry(
self, title, row=None, column=0, colspan=0, rowspan=0, secret=False)
def addValidationEntry(self, title, row=None, column=0, colspan=0, rowspan=0, secret=False): return self._entryMaker(title, row, column, colspan, rowspan, secret=False, label=False, kind="validation")
def addVerticalSeparator(
self, row=None, column=0, colspan=0, rowspan=0, colour=None)
def addVerticalSeparator(self, row=None, column=0, colspan=0, rowspan=0, colour=None): return self._addSeparator("vertical", row, column, colspan, rowspan, colour)
def addWebLink(
self, title, page, row=None, column=0, colspan=0, rowspan=0)
adds a hyperlink to the specified web page
def addWebLink(self, title, page, row=None, column=0, colspan=0, rowspan=0): ''' adds a hyperlink to the specified web page ''' link = self._buildLink(title) link.registerWebpage(page) self._positionWidget(link, row, column, colspan, rowspan) return link
def addWidget(
self, title, widg, row=None, column=0, colspan=0, rowspan=0)
adds a generic widget to the appJar grid manager
def addWidget(self, title, widg, row=None, column=0, colspan=0, rowspan=0): ''' adds a generic widget to the appJar grid manager ''' self.widgetManager.verify(WIDGET_NAMES.Widget, title) self._positionWidget(widg, row, column, colspan, rowspan) self.widgetManager.add(WIDGET_NAMES.Widget, title, widg)
def after(
self, delay_ms, callback=None, *args)
wrapper for topLevel after function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled
def after(self, delay_ms, callback=None, *args): """ wrapper for topLevel after function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled """ return self.topLevel.after(delay_ms, callback, *args)
def afterCancel(
self, afterId)
wrapper for topLevel after_cancel function tries to cancel the specified callback
def afterCancel(self, afterId): """ wrapper for topLevel after_cancel function tries to cancel the specified callback """ return self.after_cancel(afterId)
def afterIdle(
self, callback, *args)
wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled
def afterIdle(self, callback, *args): """ wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled """ return self.after_idle(callback, *args)
def after_cancel(
self, afterId)
wrapper for topLevel after_cancel function tries to cancel the specified callback
def after_cancel(self, afterId): """ wrapper for topLevel after_cancel function tries to cancel the specified callback """ return self.topLevel.after_cancel(afterId)
def after_idle(
self, callback, *args)
wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled
def after_idle(self, callback, *args): """ wrapper for topLevel after_idle function schedules the callback function to happen in x seconds returns an ID, allowing the event to be cancelled """ return self.topLevel.after_idle(callback, *args)
def appJarAbout(
self, menu=None)
def appJarAbout(self, menu=None): self.infoBox("About appJar", "---\n" + __copyright__ + "\n" + "---\n\t" + gui.SHOW_VERSION().replace("\n", "\n\t") + "\n" + "---\n" + gui.SHOW_PATHS() + "\n" + "---")
def appJarHelp(
self, menu=None)
def appJarHelp(self, menu=None): self.infoBox("appJar Help", "For help, visit " + __url__)
def appendAutoEntry(
self, title, value)
def appendAutoEntry(self, title, value): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.addWords(value) except AttributeError: gui.error("You can only append items to an AutoEntry, %s is not an AutoEntry.", title)
def bell(
self)
def bell(self): self.containerStack[0]['container'].bell()
def bindKey(
self, key, func, replace=False)
bind the specified key, to the specified function, for all widgets
def bindKey(self, key, func, replace=False): """ bind the specified key, to the specified function, for all widgets """ if replace: try: self.unbindKey(key) except: pass # for now discard the Event... myF = self.MAKE_FUNC(func, key) binding = EventBinding(key, myF, self._getTopLevel(), menuBinding=False) try: self.widgetManager.add(WIDGET_NAMES.Bindings, binding.displayName, binding) binding.createBindings() except ItemLookupError: raise ItemLookupError('Unable to bind key ' + binding.displayName + ' - binding already exists')
def bindKeys(
self, keys, func)
bind the specified keys, to the specified function, for all widgets
def bindKeys(self, keys, func): """ bind the specified keys, to the specified function, for all widgets """ for key in keys: self.bindKey(key, func)
def button(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets buttons all in one go
def buttons(
self, names, funcs, **kwargs)
def callback(
self, *args, **kwargs)
Shortner for threadCallback.
def callback(self, *args, **kwargs): """Shortner for threadCallback.""" return self.threadCallback(*args, **kwargs)
def canvas(
self, title, *args, **kwargs)
simpleGUI - adds, sets & gets canases all in one go
def canvas(self, title, *args, **kwargs): """ simpleGUI - adds, sets & gets canases all in one go """ widgKind = WIDGET_NAMES.Canvas submit = kwargs.pop("submit", None) _map = kwargs.pop("map", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists # NB. no SETTER canvas = self.getCanvas(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) canvas = self._canvasMaker(title, *args, **kwargs) if submit is not None and _map is not None: self.setCanvasMap(title, submit, _map) else: gui.warn("Must specify a submit function when setting a canvas map: %s", title) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) self._configWidget(title, widgKind, **kwargs) return canvas
def changeAutoEntry(
self, title, value)
def changeAutoEntry(self, title, value): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.changeWords(value) except AttributeError: gui.error("You can only change items in an AutoEntry, %s is not an AutoEntry.", title)
def changeLanguage(
self, language)
changes the language used by the GUI will iterate through all widgets and update their text as well as populate a translation dictionary for later lookups
def changeLanguage(self, language): """ changes the language used by the GUI will iterate through all widgets and update their text as well as populate a translation dictionary for later lookups """ self._loadConfigParser() if not ConfigParser: self.error("Internationalisation not supported") return fileName = language.upper() + ".ini" gui.trace("Loading language file: %s", fileName) if not PYTHON2: try: with codecs.open(fileName, "r", "utf8") as langFile: self.configParser.read_file(langFile) except FileNotFoundError: self.error("Invalid language, file not found: %s", fileName) return else: try: try: with codecs.open(fileName, "r", "utf8") as langFile: self.configParser.read_file(langFile) except AttributeError: with codecs.open(fileName, "r", "utf8") as langFile: self.configParser.readfp(langFile) except IOError: self.error("Invalid language, file not found: %s", fileName) return except ParsingError: self.error("Translation failed - language file contains errors, ensure there is no whitespace at the beginning of any lines.") return gui.trace("Switching to: %s", language) self._language = language self.translations = {"POPUP":{}, "SOUND":{}, "EXTERNAL":{}} # loop through each section, get the relative set of widgets # change the text for section in self.configParser.sections(): getWidgets = True section = section.upper() gui.trace("\tSection: %s", section) # convert the section title to its code if section == "CONFIG": # skip the config section (for now) gui.trace("\tSkipping CONFIG") continue elif section == "TITLE": kind = WIDGET_NAMES.SubWindow elif section.startswith("TOOLTIP-"): kind = "TOOLTIP" getWidgets = False elif section in ["SOUND", "EXTERNAL", "POPUP"]: for (key, val) in self.configParser.items(section): if section == "POPUP": val = val.strip().split("\n") self.translations[section][key] = val gui.trace("\t\t%s: %s", key, val) continue elif section == "MENUBAR": for (key, val) in self.configParser.items(section): key = key.strip().split("-") gui.trace("\t\t%s: %s", key, val) if len(key) == 1: try: self.renameMenu(key[0], val) except: self.warn("Invalid key") elif len(key) == 2: try: self.renameMenuItem(key[0], key[1], val) except: self.warn("Invalid key") continue else: try: kind = WIDGET_NAMES.getIgnoreCase(section) except Exception: self.warn("Invalid config section: %s", section) continue # if necessary, use the code to get the widget list if getWidgets: widgets = self.widgetManager.group(kind) if kind in [WIDGET_NAMES.Scale]: self.warn("No text is displayed in %s. Maybe it has a Label?", section) continue elif kind in [WIDGET_NAMES.TextArea, WIDGET_NAMES.Meter, WIDGET_NAMES.PieChart, WIDGET_NAMES.Tree]: self.warn("No text is displayed in %s", section) continue elif kind in [WIDGET_NAMES.name(WIDGET_NAMES.SubWindow)]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) if key.lower() == "appjar": self.setTitle(val) elif key.lower() == "splash": if self.splashConfig is not None: gui.trace("\t\t Updated SPLASH to: %s", val) self.splashConfig['text'] = val else: gui.trace("\t\t No SPLASH to update") elif key.lower() == "statusbar": gui.trace("\tSetting STATUSBAR: %s", val) self.setStatusbarHeader(val) else: try: widgets[key].title(val) except KeyError: self.warn("Invalid SUBWINDOW: %s", key) elif kind in [WIDGET_NAMES.ListBox]: for k in widgets.keys(): lb = widgets[k] # convert data to a list if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = lb.DEFAULT_TEXT data = data.strip().split("\n") # tidy up the list data = [item.strip() for item in data if len(item.strip()) > 0] self.updateListBox(k, data) elif kind in [WIDGET_NAMES.SpinBox]: for k in widgets.keys(): sb = widgets[k] # convert data to a list if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = sb.DEFAULT_TEXT data = data.strip().split("\n") # tidy up the list data = [item.strip() for item in data if len(item.strip()) > 0] self.changeSpinBox(k, data) elif kind in [WIDGET_NAMES.OptionBox]: for k in widgets.keys(): ob = widgets[k] # convert data to a list if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = ob.DEFAULT_TEXT data = data.strip().split("\n") # tidy up the list data = [item.strip() for item in data if len(item.strip()) > 0] self.changeOptionBox(k, data) elif kind in [WIDGET_NAMES.RadioButton]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid RADIOBUTTON key: %s", key) else: try: rbs = self.widgetManager.get(WIDGET_NAMES.RadioButton, keys[0]) except KeyError: self.warn("Invalid RADIOBUTTON key: %s", keys[0]) continue for rb in rbs: if rb.DEFAULT_TEXT == keys[1]: rb["text"] = val break elif kind in [WIDGET_NAMES.TabbedFrame]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid TABBEDFRAME key: %s", key) else: try: self.setTabText(keys[0], keys[1], val) except ItemLookupError: self.warn("Invalid TABBEDFRAME: %s with TAB: %s" , keys[0], keys[1]) elif kind in [WIDGET_NAMES.Properties]: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid PROPERTIES key: %s", key) else: try: self.setPropertyText(keys[0], keys[1], val) except ItemLookupError: self.warn("Invalid PROPERTIES: %s", keys[0]) except KeyError: self.warn("Invalid PROPERTY: %s", keys[1]) elif kind == WIDGET_NAMES.Tree: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid GRID key: %s", key) else: if keys[1] not in ["actionHeading", "actionButton", "addButton"]: self.warn("Invalid GRID label: %s for GRID: %s", keys[1], keys[0]) else: try: self.confGrid(keys[0], keys[1], val) except ItemLookupError: self.warn("Invalid GRID: %s", keys[0]) elif kind == self.PAGEDWINDOW: for (key, val) in self.configParser.items(section): gui.trace("\t\t%s: %s", key, val) keys = key.split("-") if len(keys) != 2: self.warn("Invalid PAGEDWINDOW key: %s", key) else: if keys[1] not in ["prevButton", "nextButton", "title"]: self.warn("Invalid PAGEDWINDOW label: %s for PAGEDWINDOW: %s", keys[1], keys[0]) else: try: widgets[keys[0]].config(**{keys[1]:val}) except KeyError: self.warn("Invalid PAGEDWINDOW: %s", keys[0]) elif kind == WIDGET_NAMES.Entry: for k in widgets.keys(): ent = widgets[k] if self.configParser.has_option(section, k): data = self.configParser.get(section, k) else: data = ent.DEFAULT_TEXT gui.trace("\t\t%s: %s", k, data) self.setEntryDefault(k, data) elif kind in [WIDGET_NAMES.Image]: for k in widgets.keys(): if self.configParser.has_option(section, k): data = str(self.configParser.get(section, k)) try: self.setImage(k, data) gui.trace("\t\t%s: %s", k, data) except: self.error("Failed to update image: %s to: %s", k, data) else: gui.trace("No translation for: %s", k) elif kind in [WIDGET_NAMES.Label, WIDGET_NAMES.Button, WIDGET_NAMES.CheckBox, WIDGET_NAMES.Message, WIDGET_NAMES.Link, WIDGET_NAMES.LabelFrame, self.TOGGLEFRAME]: for k in widgets.keys(): widg = widgets[k] # skip validation labels - we don't need to translate them try: if kind == WIDGET_NAMES.Label and widg.isValidation: gui.trace("\t\t%s: skipping, validation label", k) continue except: pass if self.configParser.has_option(section, k): data = str(self.configParser.get(section, k)) else: data = widg.DEFAULT_TEXT gui.trace("\t\t%s: %s", k, data) widg.config(text=data) elif kind == WIDGET_NAMES.Toolbar: for k in widgets.keys(): but = widgets[k] if but.image is None: if self.configParser.has_option(section, k): data = str(self.configParser.get(section, k)) else: data = but.DEFAULT_TEXT gui.trace("\t\t%s: %s", k, data) but.config(text = data) elif kind == "TOOLTIP": try: kind = WIDGET_NAMES.name(WIDGET_NAMES.getIgnoreCase(section.split("-")[1])) func = getattr(self, "set"+kind+"Tooltip") except KeyError: self.warn("Invalid config section: TOOLTIP-%s", section) return gui.trace("Parsing TOOLTIPs for: %s", kind) for (key, val) in self.configParser.items(section): try: func(key, val) except ItemLookupError: self.warn("Invalid TOOLTIP for: %s, with key: %s", kind, key) continue else: self.warn("Unsupported widget: %s", section) continue
def changeOptionBox(
self, title, options, index=None, callFunction=False)
Changes the entire contents of the named OptionBox ref: http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html
:param title: the OptionBox to change :param options: the new values to put in the OptionBox :param index: an optional initial value to select :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found
def changeOptionBox(self, title, options, index=None, callFunction=False): """ Changes the entire contents of the named OptionBox ref: http://www.prasannatech.net/2009/06/tkinter-optionmenu-changing-choices.html :param title: the OptionBox to change :param options: the new values to put in the OptionBox :param index: an optional initial value to select :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ # get the optionBox & associated var box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) # tidy up list and get max size maxSize, options = self._configOptionBoxList(title, options, "normal") # warn if new options bigger if maxSize > box.maxSize: self.warn("The new options are wider then the old ones: %s > %s", maxSize, box.maxSize) if box.kind == "ticks": self._buildTickOptionBox(title, box, options) else: # delete the current options box['menu'].delete(0, 'end') # add the new items for option in options: box["menu"].add_command( label=option, command=lambda temp=option: box.setvar( box.cget("textvariable"), value=temp)) with PauseCallFunction(callFunction, box): box.var.set(options[0]) box.options = options # disable any separators self._disableOptionBoxSeparators(box) # select the specified option self.setOptionBox(title, index, callFunction=False, override=True)
def changeSpinBox(
self, title, vals, reverse=True)
def changeSpinBox(self, title, vals, reverse=True): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) if spin.isRange: self.warn("Can't convert %s RangeSpinBox to SpinBox", title) else: self._populateSpinBox(spin, vals, reverse) self.setSpinBoxPos(title, 0)
def check(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for checkBox()
def check(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for checkBox() """ return self.checkBox(title, value, *args, **kwargs)
def checkBox(
self, title, value=None, *args, **kwargs)
adds, sets & gets checkBoxes all in one go
def checkBox(self, title, value=None, *args, **kwargs): """ adds, sets & gets checkBoxes all in one go """ widgKind = WIDGET_NAMES.CheckBox callFunction = kwargs.pop("callFunction", True) text = kwargs.pop("text", None) try: self.widgetManager.verify(widgKind, title) except: #widget exists if value is not None: self.setCheckBox(title, ticked=value, callFunction=callFunction) check = self.getCheckBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) check = self._checkBoxMaker(title, *args, **kwargs) if value is not None: self.setCheckBox(title, value) if text is not None: self.setCheckBoxText(title, text) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return check
def cleanseWidgets(
self, widget)
def cleanseWidgets(self, widget): widgType = gui.GET_WIDGET_CLASS(widget) gui.trace("Attempting to cleanse: %s", widgType) # make sure we've cleansed any children first for child in widget.winfo_children(): self.cleanseWidgets(child) if hasattr(widget, 'APPJAR_TYPE'): widgType = widget.APPJAR_TYPE widgName = WIDGET_NAMES.name(widgType) gui.trace("Cleansing: %s", widgName) if widgType not in [WIDGET_NAMES.Tab, WIDGET_NAMES.Page]: if not self.widgetManager.destroyWidget(widgType, widget): self.warn("Unable to destroy %s, during cleanse - destroy returned False", widgName) # must clear the frameLabel's label as well if widgType == WIDGET_NAMES.FrameLabel: gui.trace("Also Cleansing: %s", WIDGET_NAMES.name(WIDGET_NAMES.Label)) if not self.widgetManager.destroyWidget(WIDGET_NAMES.Label, widget): self.warn("Unable to destroy %s, during cleanse - destroy returned False", WIDGET_NAMES.Label) else: self.trace("Skipped %s, cleansed by parent", widgType) # need to remove if a container if widgName in WIDGET_NAMES.containers: self.trace("Destroying container: %s", widgName) self.widgetManager.destroyContainer(WIDGET_NAMES.ContainerLog, widget) elif widgType in ('CanvasDnd', 'ValidationLabel', 'TabBorder', 'TabContainer', 'TabText', 'BgLabel') or hasattr(widget, 'SKIP_CLEANSE'): elif widgType in ('CanvasDnd', 'ValidationLabel', 'Grip', 'TabBorder', 'TabContainer', 'TabText', 'BgLabel') \ or widget.__dict__.get('SKIP_CLEANSE', False): pass # not logged in WidgetManager else: self.warn("Unable to destroy %s, during cleanse - NO APPJAR TYPE", gui.GET_WIDGET_CLASS(widget))
def clearAllCheckBoxes(
self, callFunction=False)
def clearAllCheckBoxes(self, callFunction=False): for cb in self.widgetManager.group(WIDGET_NAMES.CheckBox): self.setCheckBox(cb, ticked=False, callFunction=callFunction)
def clearAllDatePickers(
self, callFunction=False)
def clearAllDatePickers(self, callFunction=False): for k in self.widgetManager.group(WIDGET_NAMES.DatePicker): self.clearDatePicker(k, callFunction)
def clearAllEntries(
self, callFunction=False)
def clearAllEntries(self, callFunction=False): for entry in self.widgetManager.group(WIDGET_NAMES.Entry, group=WidgetManager.VARS): self.clearEntry(entry, callFunction=callFunction, setFocus=False)
def clearAllLabels(
self)
def clearAllLabels(self): for lb in self.widgetManager.group(WIDGET_NAMES.Label): self.clearLabel(lb)
def clearAllListBoxes(
self, callFunction=False)
def clearAllListBoxes(self, callFunction=False): for lb in self.widgetManager.group(WIDGET_NAMES.ListBox): self.clearListBox(lb, callFunction)
def clearAllOptionBoxes(
self, callFunction=False)
Convenience function to clear all OptionBoxes in the GUI Will simply call clearOptionBox on each OptionBox/TickOptionBox
:param callFunction: whether to generate an event to notify that the widget has changed :returns: None
def clearAllOptionBoxes(self, callFunction=False): """ Convenience function to clear all OptionBoxes in the GUI Will simply call clearOptionBox on each OptionBox/TickOptionBox :param callFunction: whether to generate an event to notify that the widget has changed :returns: None """ for k in self.widgetManager.group(WIDGET_NAMES.OptionBox): self.clearOptionBox(k, callFunction)
def clearAllProperties(
self, callFunction=False)
def clearAllProperties(self, callFunction=False): props = {} for k in self.widgetManager.group(WIDGET_NAMES.Properties): self.clearProperties(k, callFunction)
def clearAllRadioButtons(
self, callFunction=False)
def clearAllRadioButtons(self, callFunction=False): for rb in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS): self.setRadioButton(rb, self.widgetManager.get(WIDGET_NAMES.RadioButton, rb, group=WidgetManager.VARS).startVal, callFunction=callFunction)
def clearAllScales(
self, callFunction=False)
def clearAllScales(self, callFunction=False): for sc in self.widgetManager.group(WIDGET_NAMES.Scale): self.setScale(sc, self.widgetManager.get(WIDGET_NAMES.Scale, sc).cget("from"), callFunction=callFunction)
def clearAllSpinBoxes(
self, callFunction=False)
def clearAllSpinBoxes(self, callFunction=False): for sb in self.widgetManager.group(WIDGET_NAMES.SpinBox): self.setSpinBoxPos(sb, 0, callFunction=callFunction)
def clearAllTextAreas(
self, callFunction=False)
Convenience function to clear all TextAreas in the GUI Will simply call clearTextArea on each TextArea
:param callFunction: whether to generate an event to notify that the widget has changed :returns: None
def clearAllTextAreas(self, callFunction=False): """ Convenience function to clear all TextAreas in the GUI Will simply call clearTextArea on each TextArea :param callFunction: whether to generate an event to notify that the widget has changed :returns: None """ for ta in self.widgetManager.group(WIDGET_NAMES.TextArea): self.clearTextArea(ta, callFunction=callFunction)
def clearCanvas(
self, title)
def clearCanvas(self, title): self.widgetManager.get(WIDGET_NAMES.Canvas, title).delete("all")
def clearDatePicker(
self, title, callFunction=True)
def clearDatePicker(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) self.setOptionBox(title + "_DP_YearOptionBox", 0, callFunction) self.setOptionBox(title + "_DP_MonthOptionBox", 0, callFunction) self.setOptionBox(title + "_DP_DayOptionBox", 0, callFunction)
def clearEntry(
self, name, callFunction=True, setFocus=True)
def clearEntry(self, name, callFunction=True, setFocus=True): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) # now call function with PauseCallFunction(callFunction, var, False): var.set("") self._updateEntryDefault(name, mode="clear") if setFocus: self.setFocus(name)
def clearImageCache(
self)
def clearImageCache(self): self.widgetManager.clear(WIDGET_NAMES.ImageCache)
def clearLabel(
self, name)
def clearLabel(self, name): self.setLabel(name, "")
def clearListBox(
self, title, callFunction=True)
def clearListBox(self, title, callFunction=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.selection_clear(0, END) lb.delete(0, END) # clear if callFunction and hasattr(lb, 'cmd'): lb.cmd()
def clearMessage(
self, title)
def clearMessage(self, title): self.setMessage(title, "")
def clearMicroBit(
self, title)
def clearMicroBit(self, title): self.widgetManager.get(WIDGET_NAMES.MicroBit, title).clear()
def clearOptionBox(
self, title, callFunction=True)
Deselects any items selected in the named OptionBox If a TickOptionBox, all items will be set to False (unticked)
:param title: the OptionBox to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found
def clearOptionBox(self, title, callFunction=True): """ Deselects any items selected in the named OptionBox If a TickOptionBox, all items will be set to False (unticked) :param title: the OptionBox to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) if box.kind == "ticks": # loop through each tick, set it to False ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) for k in ticks: self.setOptionBox(title, k, False, callFunction=callFunction) else: self.setOptionBox(title, 0, callFunction=callFunction, override=True)
def clearProperties(
self, title, callFunction=True)
def clearProperties(self, title, callFunction=True): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.clearProperties(callFunction)
def clearSpinBox(
self, title, callFunction=False)
def clearSpinBox(self, title, callFunction=False): self.setSpinBoxPos(title, 0, callFunction=callFunction)
def clearStatusbar(
self, field=None)
def clearStatusbar(self, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(text=self._getFormatStatus("")) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(text=self._getFormatStatus("")) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1))
def clearTextArea(
self, title, callFunction=True)
Removes all text from the specified TextArea
:param title: the TextArea to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found
def clearTextArea(self, title, callFunction=True): """ Removes all text from the specified TextArea :param title: the TextArea to change :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.pauseCallFunction(callFunction) # in case it's disabled _state = ta.cget('state') ta.config(state='normal') ta.delete('1.0', END) ta.config(state=_state) ta.resumeCallFunction()
def clearTree(
self, title)
def clearTree(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.destroy() tree.update()
def colourBox(
self, colour='#ff0000', parent=None)
def colourBox(self, colour='#ff0000', parent=None): self.topLevel.update_idletasks() if parent is None: col = askcolor(colour) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} col = askcolor(colour, **opts) if col[1] is None: return None else: return col[1]
def combo(
self, title, value=None, *args, **kwargs)
shortner for optionBox()
def combo(self, title, value=None, *args, **kwargs): """ shortner for optionBox() """ return self.optionBox(title, value, *args, **kwargs)
def confGrid(
self, title, field, value)
def confGrid(self, title, field, value): return self.confTable(title, field, value)
def confTable(
self, title, field, value)
def confTable(self, title, field, value): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) kw = {field:value} grid.config(**kw)
def config(
self, **kwargs)
def config(self, **kwargs): self.configure(**kwargs)
def configure(
self, **kwargs)
def configure(self, **kwargs): title = kwargs.pop("title", None) icon = kwargs.pop("icon", None) transparency = kwargs.pop("transparency", None) visible = kwargs.pop("visible", None) top = kwargs.pop("top", None) padding = kwargs.pop("padding", None) inPadding = kwargs.pop("inPadding", None) guiPadding = kwargs.pop("guiPadding", None) size = kwargs.pop("size", None) location = kwargs.pop("location", None) fullscreen = kwargs.pop("fullscreen", None) resizable = kwargs.pop("resizable", None) sticky = kwargs.pop("sticky", None) stretch = kwargs.pop("stretch", None) expand = kwargs.pop("expand", None) row = kwargs.pop("row", None) colspan = kwargs.pop("colspan", None) rowspan = kwargs.pop("rowspan", None) fg = kwargs.pop("fg", None) bg = kwargs.pop("bg", None) font = kwargs.pop("font", None) buttonFont = kwargs.pop("buttonFont", None) labelFont = kwargs.pop("labelFont", None) inputFont = kwargs.pop("inputFont", None) statusFont = kwargs.pop("statusFont", None) ttkTheme = kwargs.pop("ttkTheme", None) editMenu = kwargs.pop("editMenu", None) # two possible names stopFunction = kwargs.pop("stop", kwargs.pop("stopFunction", None)) startFunction = kwargs.pop("start", kwargs.pop("startFunction", None)) fastStop = kwargs.pop("fastStop", None) enterKey = kwargs.pop("enterKey", None) logLevel = kwargs.pop("log", kwargs.pop("logLevel", None)) logFile = kwargs.pop("file", kwargs.pop("logFile", None)) language = kwargs.pop("language", None) for k, v in kwargs.items(): gui.error("Invalid config parameter: %s, %s", k, v) if title is not None: self.title = title if icon is not None: self.icon = icon if transparency is not None: self.transparency = transparency if visible is not None: self.visible = visible if top is not None: self.top = top if padding is not None: self.padding = padding if inPadding is not None: self.inPadding = inPadding if guiPadding is not None: self.guiPadding = guiPadding if size is not None: self.size = size if location is not None: self.location = location if fullscreen is not None: self.fullscreen = fullscreen if resizable is not None: self.resizable = resizable if sticky is not None: self.sticky = sticky if expand is not None: self.expand = expand if stretch is not None: self.stretch = stretch if row is not None: self.row = row if rowspan is not None: self.rowspan = rowspan if colspan is not None: self.colspan = colspan if fg is not None: self.fg = fg if bg is not None: self.bg = bg if font is not None: self.font = font if labelFont is not None: self.labelFont = labelFont if buttonFont is not None: self.buttonFont = buttonFont if inputFont is not None: self.inputFont = inputFont if statusFont is not None: self.statusFont = statusFont if ttkTheme is not None: self.ttkTheme = ttkTheme if editMenu is not None: self.editMenu = editMenu if stopFunction is not None: self.stopFunction = stopFunction if startFunction is not None: self.startFunction = startFunction if fastStop is not None: self.fastStop = fastStop if enterKey is not None: self.enterKey = enterKey if logLevel is not None: self.logLevel = logLevel if logFile is not None: self.logFile = logFile if language is not None: self.language = language
def configureAllWidgets(
self, kind, option, value)
def configureAllWidgets(self, kind, option, value): items = list(self.widgetManager.group(kind)) self.configureWidgets(kind, items, option, value)
def configureWidget(
self, kind, name, option, value, key=None, deprecated=False)
def configureWidget(self, kind, name, option, value, key=None, deprecated=False): gui.trace("Configuring: %s of %s with %s of %s", name, kind, option, value) # warn about deprecated functions if deprecated: self.warn("Deprecated config function (%s) used for %s -> %s use %s deprecated", option, WIDGET_NAMES.name(kind), name, deprecated) # will return multiple items if radio button... items = self._getWidgetList(kind, name, limit=option in ['change', 'command']) # loop through each item, and try to reconfigure it #Â this will often fail - widgets have varied config options for item in items: try: if option == 'background': gui.SET_WIDGET_BG(item, value, True) elif option == 'foreground': gui.SET_WIDGET_FG(item, value, True) elif option == 'disabledforeground': item.config(disabledforeground=value) elif option == 'disabledbackground': item.config(disabledbackground=value) elif option == 'activeforeground': item.config(activeforeground=value) elif option == 'activebackground': item.config(activebackground=value) elif option == 'inactiveforeground': if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]: item.config(inactiveforeground=value) else: self.warn("Error configuring %s: can't set inactiveforeground", name ) elif option == 'inactivebackground': if kind in [WIDGET_NAMES.TabbedFrame, WIDGET_NAMES.Table]: item.config(inactivebackground=value) else: self.warn("Error configuring %s: can't set inactivebackground", name) elif option == 'width': item.config(width=value) elif option == 'height': item.config(height=value) elif option == 'state': # make entries readonly - can still copy/paste but = None if kind == WIDGET_NAMES.Entry: if value == "disabled" and hasattr(item, 'but'): but = item.but item.unbind("<Button-1>") value = "readonly" elif value == 'normal' and hasattr(item, 'but') and item.cget('state') != 'normal': but = item.but item.bind("<Button-1>", item.click_command, "+") if self.ttkFlag: gui.trace("%s configured with ttk state %s", name, value) item.state([value]) if but is not None: but.state([value]) else: item.config(state=value) if but is not None: but.config(state=value) elif option == 'relief': item.config(relief=value) elif option == 'style': if self.ttkFlag: gui.trace("%s configured with ttk style %s", name, value) item.config(style=value) else: self.warn("Error configuring %s: can't set ttk style, not in ttk mode.", name) elif option in ['align', 'anchor']: if kind == WIDGET_NAMES.Entry or gui.GET_WIDGET_CLASS(item) == 'SelectableLabel': if value == W: value = LEFT elif value == E: value = RIGHT item.config(justify=value) elif kind == WIDGET_NAMES.LabelFrame: item.config(labelanchor=value) else: if value == LEFT: value = "w" elif value == RIGHT: value = "e" item.config(anchor=value) elif option == 'cursor': item.config(cursor=value) elif option == 'tooltip': self._addTooltip(item, value) elif option == 'disableTooltip': self._disableTooltip(item) elif option == 'enableTooltip': self._enableTooltip(item) elif option == "focus": item.focus_set() if kind == WIDGET_NAMES.Entry: if not self.ttkFlag: item.icursor(END) item.xview(END) else: item.icursor(END) item.xview(len(item.get())) # event bindings elif option == 'over': self._bindOverEvent(kind, name, item, value, option, key) elif option == 'drag': self._bindDragEvent(kind, name, item, value, option, key) elif option in ['command', "change", "submit"]: self._bindEvent(kind, name, item, value, option, key) elif option == 'sticky': info = {} # need to reposition the widget in its grid if self._widgetHasContainer(kind, item): # pack uses LEFT & RIGHT & BOTH info["side"] = value if value.lower() == "both": info["expand"] = 1 info["side"] = "right" else: info["expand"] = 0 else: # grid uses E+W if value.lower() == "left": side = W elif value.lower() == "right": side = E elif value.lower() == "both": side = W + E else: side = value.upper() info["sticky"] = side self._repackWidget(item, info) elif option == 'padding': if value[1] is None: item.config(padx=value[0][0], pady=value[0][1]) else: item.config(padx=value[0], pady=value[1]) elif option == 'ipadding': if value[1] is None: item.config(ipadx=value[0][0], ipady=value[0][1]) else: item.config(ipadx=value[0], ipady=value[1]) elif option == 'rightClick': self._bindRightClick(item, value) elif option == 'internalDrop': self._registerInternalDropTarget(item, value) elif option == 'internalDrag': self._registerInternalDragSource(kind, name, item, value) elif option == 'externalDrop': self._registerExternalDropTarget(name, item, value[0], value[1]) elif option == 'externalDrag': self._registerExternalDragSource(name, item, value) except TclError as e: self.warn("Error configuring %s: %s", name, str(e))
def configureWidgets(
self, kind, names, option, value)
def configureWidgets(self, kind, names, option, value): if not isinstance(names, list): self.configureWidget(kind, names, option, value) else: for widg in names: # incase 2D array, eg. buttons if isinstance(widg, list): for widg2 in widg: self.configureWidget(kind, widg2, option, value) else: self.configureWidget(kind, widg, option, value)
def confirmHideSubWindow(
self, title)
def confirmHideSubWindow(self, title): self.hideSubWindow(title, True)
def convertJpgToBmp(
self, image)
def convertJpgToBmp(self, image): self._loadNanojpeg() if nanojpeg is False: raise Exception( "nanojpeg library not found, unable to display jpeg files: " + image) elif sys.version_info < (2, 7): raise Exception( "JPG images only supported in python 2.7+: " + image) else: # read the image into an array of bytes with open(image, 'rb') as inFile: buf = array.array(str('B'), inFile.read()) # init the translator, and decode the array of bytes nanojpeg.njInit() nanojpeg.njDecode(buf, len(buf)) # determine a file name & type if nanojpeg.njIsColor(): fileName = image.split('.jpg', 1)[0] + '.ppm' param = 6 else: fileName = image.split('.jpg', 1)[0] + '.pgm' fileName = "test3.pgm" param = 5 # create a string, starting with the header val = "P%d\n%d %d\n255\n" % ( param, nanojpeg.njGetWidth(), nanojpeg.njGetHeight()) # append the bytes, converted to chars val = str(val) + str('').join(map(chr, nanojpeg.njGetImage())) # release any stuff nanojpeg.njDone() photo = PhotoImage(data=val) return photo
def countFrames(
self, title)
def countFrames(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getNumFrames()
def createMenu(
self, title, tearable=False, showInBar=True)
def createMenu(self, title, tearable=False, showInBar=True): self.widgetManager.verify(WIDGET_NAMES.Menu, title) self._initMenu() if title == "WIN_SYS" and self.platform != self.WINDOWS: self.warn("The WIN_SYS menu is specific to Windows") return None if self.platform == self.MAC and tearable: self.warn("Tearable menus (%s) not supported on MAC", title) tearable = False theMenu = Menu(self.menuBar, tearoff=tearable) if showInBar: self.menuBar.add_cascade(label=title, menu=theMenu) self.widgetManager.add(WIDGET_NAMES.Menu, title, theMenu) return theMenu
def createRightClickMenu(
self, title, showInBar=False)
def createRightClickMenu(self, title, showInBar=False): men = self.createMenu(title, False, showInBar) men.bind("<FocusOut>", lambda e: men.unpost()) return men
def critical(
message, *args)
wrapper for logMessage - setting level to CRITICAL
@staticmethod def critical(message, *args): """ wrapper for logMessage - setting level to CRITICAL """ gui.logMessage(message, "CRITICAL", *args)
def date(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for datePicker()
def date(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for datePicker() """ return self.datePicker(title, value, *args, **kwargs)
def datePicker(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets datePickers all in one go
def datePicker(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets datePickers all in one go """ widgKind = WIDGET_NAMES.DatePicker change = kwargs.pop("change", None) toValue = kwargs.pop("toValue", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists dp = self.getDatePicker(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) dp = self.addDatePicker(title, *args, **kwargs) if value is not None: if toValue is None: self.setDatePicker(title, value) else: self.setDatePickerRange(title, startYear=value, endYear=toValue) if change is not None: self.setDatePickerChangeFunction(title, change) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return dp
def debug(
message, *args)
wrapper for logMessage - setting level to DEBUG
@staticmethod def debug(message, *args): """ wrapper for logMessage - setting level to DEBUG """ gui.logMessage(message, "DEBUG", *args)
def decreaseButtonFont(
self)
def decreaseButtonFont(self): self.setButtonFont(size=self._buttonFont['size'] - 1)
def decreaseFont(
self)
def decreaseFont(self): self.decreaseLabelFont() self.decreaseButtonFont()
def decreaseLabelFont(
self)
def decreaseLabelFont(self): self.setLabelFont(size=self._labelFont['size'] - 1)
def deleteAllGridRows(
self, title)
def deleteAllGridRows(self, title): return self.deleteAllTableRows(title)
def deleteAllTableRows(
self, title)
def deleteAllTableRows(self, title): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteAllRows()
def deleteGridColumn(
self, title, columnNumber)
def deleteGridColumn(self, title, columnNumber): return self.deleteTableColumn(title, columnNumber)
def deleteGridRow(
self, title, rowNum)
def deleteGridRow(self, title, rowNum): return self.deleteTableRow(title, rowNum)
def deleteMenuItem(
self, title, item)
def deleteMenuItem(self, title, item): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: bindingName = theMenu.entrycget(item, 'accelerator') theMenu.delete(item) self.widgetManager.get(WIDGET_NAMES.Bindings, bindingName).removeBindings() self.widgetManager.remove(WIDGET_NAMES.Bindings, bindingName) except TclError: gui.error("Unable to delete menu item: %s, in menu: %s - item not found", item, title)
def deleteOptionBox(
self, title, index)
Deleted the specified item from the named OptionBox
:param title: the OptionBox to change :param inde: the value to delete - either a numeric index, or the text of an item :returns: None :raises ItemLookupError: if the title can't be found
def deleteOptionBox(self, title, index): """ Deleted the specified item from the named OptionBox :param title: the OptionBox to change :param inde: the value to delete - either a numeric index, or the text of an item :returns: None :raises ItemLookupError: if the title can't be found """ self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) self.setOptionBox(title, index, value=None, override=True)
def deleteProperty(
self, title, prop)
def deleteProperty(self, title, prop): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.addProperty(prop, None, callFunction=False)
def deleteTabbedFrameTab(
self, title, tab)
def deleteTabbedFrameTab(self, title, tab): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) self.cleanseWidgets(nb.getTab(tab)) nb.deleteTab(tab)
def deleteTableColumn(
self, title, columnNumber)
deletes the specified column from the specified table
def deleteTableColumn(self, title, columnNumber): ''' deletes the specified column from the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteColumn(columnNumber)
def deleteTableRow(
self, title, rowNum)
def deleteTableRow(self, title, rowNum): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteRow(rowNum)
def deselectAllListItems(
self, title, callFunction=False)
def deselectAllListItems(self, title, callFunction=False): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.selection_clear(0, END) if callFunction and hasattr(lb, 'cmd'): lb.cmd()
def deselectListItemAtPos(
self, title, pos, callFunction=False)
def deselectListItemAtPos(self, title, pos, callFunction=False): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if lb.size() == 0: gui.warn("No items in list: %s, unable to deselect item at pos: %s", title, pos) return False if pos < 0 or pos > lb.size() - 1: gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1) return False lb.selection_clear(pos) if callFunction and hasattr(lb, 'cmd'): lb.cmd() self.topLevel.update_idletasks() return True
def destroyAllSubWindows(
self)
def destroyAllSubWindows(self): gui.trace("Destroying all SubWindows") keys = list(self.widgetManager.group(WIDGET_NAMES.SubWindow).keys()) for k in keys: gui.trace("Destroying SubWindow: %s", k) wi = self.widgetManager.get(WIDGET_NAMES.SubWindow, k) self.cleanseWidgets(wi) # access has widgets stored in the standard widget store self.accessMade = False
def destroySubWindow(
self, title)
def destroySubWindow(self, title): gui.trace("Destroying SubWindow %s", title) tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) tl.prepDestroy() # get rid of all the kids! self.cleanseWidgets(tl)
def directoryBox(
self, title=None, dirName=None, parent=None)
def directoryBox(self, title=None, dirName=None, parent=None): self.topLevel.update_idletasks() options = {} options['initialdir'] = dirName options['title'] = title options['mustexist'] = False if parent is not None: options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) fileName = filedialog.askdirectory(**options) if fileName == "": return None else: return fileName
def disableEnter(
self)
unbinds
def disableEnter(self): """ unbinds <Return> from all widgets """ self.unbindKey("Return")
def disableMenu(
self, title)
def disableMenu(self, title): gui.trace('Disabling submenu: %s', title) theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) self._disableMenu(theMenu)
def disableMenuEdit(
self)
def disableMenuEdit(self): self.copyAndPaste.inUse = False
def disableMenuItem(
self, title, item)
def disableMenuItem(self, title, item): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: gui.trace("Disabling menu item: %s, in menu: %s", item, title) self._changeMenuItemState(theMenu, item, DISABLED) except TclError: gui.error("Unable to disable menu item: %s, in menu: %s - item not found", item, title)
def disableMenubar(
self)
def disableMenubar(self): gui.trace('Disabling toplevel menubar') self._disableMenu(self.menuBar)
def disableTableEntry(
self, title, entryPos, disabled=True)
def disableTableEntry(self, title, entryPos, disabled=True): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.disableEntry(entryPos, disabled=disabled)
def emptyCurrentContainer(
self)
def emptyCurrentContainer(self): cConf = self.containerStack[-1] kind = WIDGET_NAMES.name(cConf['type']) title = cConf['title'] gui.trace('Emptying current container %s: %s', kind, title) if not self._emptyContainerObj(cConf['container']): gui.trace('No widgets found in current container %s: %s to empty', kind, title) cConf = self._prepContainer(cConf["title"], cConf["type"], cConf["container"], 0, 1) self.containerStack[-1] = cConf
def emptySubWindow(
self, title)
def emptySubWindow(self, title): # not generated by function generator self._emptyContainerType(WIDGET_NAMES.SubWindow, title)
def enableEnter(
self, func, replace=False)
Binds
def enableEnter(self, func, replace=False): """ Binds <Return> to the specified function - all widgets """ self.bindKey("Return", func, replace)
def enableMenu(
self, title)
def enableMenu(self, title): gui.trace('Enabling submenu: %s', title) theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) self._enableMenu(theMenu)
def enableMenuItem(
self, title, item)
def enableMenuItem(self, title, item): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: gui.trace("Enabling menu item: %s, in menu: %s", item, title) self._changeMenuItemState(theMenu, item, NORMAL) except TclError: gui.error("Unable to enable menu item: %s, in menu: %s - item not found", item, title)
def enableMenubar(
self)
def enableMenubar(self): gui.trace('Enabling toplevel menubar') self._enableMenu(self.menuBar)
def entry(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets entries all in one go
def entry(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets entries all in one go """ widgKind = WIDGET_NAMES.Entry default = kwargs.pop("default", None) limit = kwargs.pop("limit", None) case = kwargs.pop("case", None) rows = kwargs.pop("rows", None) secret = kwargs.pop("secret", False) kind = kwargs.pop("kind", "standard").lower().strip() labBg = kwargs.pop("labBg", None) try: self.widgetManager.verify(WIDGET_NAMES.Entry, title) except: # widget exists if value is not None: self.setEntry(title, value, *args, **kwargs) ent = self.getEntry(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) # create the entry widget if kind == "auto": if value is None: value = [] ent = self._entryMaker(title, *args, secret=secret, kind=kind, words=value, **kwargs) else: ent = self._entryMaker(title, *args, secret=secret, kind=kind, **kwargs) if not ent: return # apply any setter values if limit is not None: self.setEntryMaxLength(title, limit) if case == "upper": self.setEntryUpperCase(title) elif case == "lower": self.setEntryLowerCase(title) if default is not None: self.setEntryDefault(title, default) if kind != "auto": if value is not None: self.setEntry(title, value) else: if rows is not None: self.setAutoEntryNumRows(title, rows) if labBg is not None and self.widgetManager.get(WIDGET_NAMES.Entry, title).isValidation: self.setValidationEntryLabelBg(title, labBg) # used by file entries kwargs.pop("text", None) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return ent
def error(
message, *args)
wrapper for logMessage - setting level to ERROR
@staticmethod def error(message, *args): """ wrapper for logMessage - setting level to ERROR """ gui.logMessage(message, "ERROR", *args)
def errorBox(
self, title, message, parent=None)
def errorBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: MessageBox.showerror(title, message) if self.topLevel.displayed: self._bringToFront() else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} MessageBox.showerror(title, message, **opts) self._bringToFront(parent)
def exception(
message, *args)
wrapper for logMessage - setting level to EXCEPTION
@staticmethod def exception(message, *args): """ wrapper for logMessage - setting level to EXCEPTION """ gui.logMessage(message, "EXCEPTION", *args)
def exitFullscreen(
self, container=None)
turns off fullscreen mode for the specified window
def exitFullscreen(self, container=None): """ turns off fullscreen mode for the specified window """ if container is None or isinstance(container, UNIVERSAL_STRING): try: container = self.widgetManager.get(WIDGET_NAMES.SubWindow, container) except: container = self._getTopLevel() if container.isFullscreen: container.isFullscreen = False container.attributes('-fullscreen', False) if container.escapeBindId is not None: container.unbind('<Escape>', container.escapeBindId) with PauseLogger(): self._doTitleBar() return True else: return False
def firstFrame(
self, title, callFunction=True)
def firstFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFirstFrame(callFunction)
def floatBox(
self, title, message, parent=None)
def floatBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return SimpleDialog.askfloat(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return SimpleDialog.askfloat(title=title, message=message, **opts)
def frame(
*args, **kwds)
@contextmanager def frame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): if title is None: # new subFrame fr = self.startFrame(title, row, column, colspan, rowspan, sticky) else: frameNumber = kwargs.pop('frameNumber', None) try: if frameNumber is not None: fr = self.openSubFrame(title, frameNumber) else: fr = self.openFrame(title) except: # no widget fr = self.startFrame(title, row, column, colspan, rowspan, sticky) self.configure(**kwargs) try: yield fr finally: self.stopFrame()
def frameStack(
*args, **kwds)
@contextmanager def frameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): change = kwargs.pop("change", None) start = kwargs.pop("start", -1) try: fr = self.startFrameStack(title, row, column, colspan, rowspan, sticky, change=change, start=start) except ItemLookupError: fr = self.openFrameStack(title) self.configure(**kwargs) try: yield fr finally: self.stopFrameStack()
def frameStackAtEnd(
self, title)
def frameStackAtEnd(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atEnd()
def frameStackAtStart(
self, title)
def frameStackAtStart(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).atStart()
def generateTree(
self, title)
displays data inside tree
def generateTree(self, title): """ displays data inside tree """ tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) gui.trace("Generating Tree: %s", title) tree.update() gui.trace("Tree updated: %s", title) tree.expand() gui.trace("Tree expanded: %s", title)
def getAllCheckBoxes(
self)
def getAllCheckBoxes(self): cbs = {} for k in self.widgetManager.group(WIDGET_NAMES.CheckBox): cbs[k] = self.getCheckBox(k) return cbs
def getAllDatePickers(
self)
def getAllDatePickers(self): dps = {} for k in self.widgetManager.group(WIDGET_NAMES.DatePicker): dps[k] = self.getDatePicker(k) return dps
def getAllEntries(
self)
def getAllEntries(self): entries = {} for k in self.widgetManager.group(WIDGET_NAMES.Entry): entries[k] = self.getEntry(k) return entries
def getAllInputs(
self, **kwargs)
Get all values, merge & return as a single dictionary.
:param kwargs: will be appended to the input list.
Note, empty pairs from each input is stripped, existing keys will not be overridden!
def getAllInputs(self, **kwargs): """Get all values, merge & return as a single dictionary. :param kwargs: will be _appended_ to the input list. Note, empty pairs from each input is stripped, existing keys will not be overridden! """ # used to stop removal of empty inputs includeEmptyInputs = kwargs.pop('includeEmptyInputs', False) # All available inputs. inputs = filter(None, [ self.getAllEntries(), self.getAllOptionBoxes(), self.getAllSpinBoxes(), self.getAllListBoxes(), self.getAllProperties(), self.getAllCheckBoxes(), self.getAllRadioButtons(), self.getAllScales(), self.getAllMeters(), self.getAllDatePickers(), kwargs, ]) result = data = dict() for pairs in inputs: for key, val in pairs.items(): # Try and strip values. try: val = val.strip() except AttributeError: pass try: # Skip if value is empty or if key already exists. if (not includeEmptyInputs and not val) or result[key]: continue except KeyError: pass result[key] = val return result
def getAllListBoxes(
self)
def getAllListBoxes(self): boxes = {} for k in self.widgetManager.group(WIDGET_NAMES.ListBox): boxes[k] = self.getListBox(k) return boxes
def getAllListItems(
self, title)
def getAllListItems(self, title): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) items = lb.get(0, END) return list(items)
def getAllMeters(
self)
def getAllMeters(self): meters = {} for k in self.widgetManager.group(WIDGET_NAMES.Meter): meters[k] = self.getMeter(k) return meters
def getAllOptionBoxes(
self)
Convenience function to get the selected items for all OptionBoxes in the GUI.
:returns: a dictionary containing the result of calling getOptionBox for every OptionBox/TickOptionBox in the GUI
def getAllOptionBoxes(self): """ Convenience function to get the selected items for all OptionBoxes in the GUI. :returns: a dictionary containing the result of calling getOptionBox for every OptionBox/TickOptionBox in the GUI """ boxes = {} for k in self.widgetManager.group(WIDGET_NAMES.OptionBox): boxes[k] = self.getOptionBox(k) return boxes
def getAllProperties(
self)
def getAllProperties(self): props = {} for k in self.widgetManager.group(WIDGET_NAMES.Properties): props[k] = self.getProperties(k) return props
def getAllRadioButtons(
self)
def getAllRadioButtons(self): rbs = {} for k in self.widgetManager.group(WIDGET_NAMES.RadioButton, group=WidgetManager.VARS): rbs[k] = self.getRadioButton(k) return rbs
def getAllScales(
self)
def getAllScales(self): scales = {} for k in self.widgetManager.group(WIDGET_NAMES.Scale): scales[k] = self.getScale(k) return scales
def getAllSpinBoxes(
self)
def getAllSpinBoxes(self): boxes = {} for k in self.widgetManager.group(WIDGET_NAMES.SpinBox): boxes[k] = self.getSpinBox(k) return boxes
def getAllTextAreas(
self)
Convenience function to get the text for all TextAreas in the GUI.
:returns: a dictionary containing the result of calling getTextArea for every TextArea in the GUI
def getAllTextAreas(self): """ Convenience function to get the text for all TextAreas in the GUI. :returns: a dictionary containing the result of calling getTextArea for every TextArea in the GUI """ areas = {} for k in self.widgetManager.group(WIDGET_NAMES.TextArea): areas[k] = self.getTextArea(k) return areas
def getBg(
self)
def getBg(self): if self._getContainerProperty('type') == WIDGET_NAMES.RootPage: if not self.ttkFlag: return self.bgLabel.cget("bg") else: return self.bgLabel.cget("background") else: if not self.ttkFlag: return self._getContainerProperty('container').cget("bg") else: return None
def getButton(
self, name)
def getButton(self, name): but = self.widgetManager.get(WIDGET_NAMES.Button, name) return but.cget("text")
def getButtonFont(
self)
def getButtonFont(self): return self._getContainerProperty('buttonFont').actual()
def getCanvas(
self, title)
def getCanvas(self, title): return self.widgetManager.get(WIDGET_NAMES.Canvas, title)
def getCheckBox(
self, title)
def getCheckBox(self, title): bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS) if bVar.get() == 1: return True else: return False
def getColspan(
self)
def getColspan(self): return self.containerStack[-1]['colspan']
def getContainer(
self)
def getContainer(self): container = self._getContainerProperty('container') if self._getContainerProperty('type') == WIDGET_NAMES.ScrollPane: return container.interior elif self._getContainerProperty('type') == WIDGET_NAMES.PagedWindow: return container.getPage() elif self._getContainerProperty('type') == WIDGET_NAMES.ToggleFrame: return container.getContainer() elif self._getContainerProperty('type') == WIDGET_NAMES.SubWindow: return container.canvasPane else: return container
def getCurrentFrame(
self, title)
def getCurrentFrame(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getCurrentFrame()
def getDatePicker(
self, title)
def getDatePicker(self, title): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) day = int(self.getOptionBox(title + "_DP_DayOptionBox")) month = self.MONTH_NAMES.index( self.getOptionBox( title + "_DP_MonthOptionBox")) + 1 year = int(self.getOptionBox(title + "_DP_YearOptionBox")) date = datetime.date(year, month, day) return date
def getEntry(
self, name)
def getEntry(self, name): entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) if entry.showingDefault: if entry.isNumeric: return None else: return "" else: val = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS).get() if entry.isNumeric: if len(val) == 0 or (len(val) == 1 and val in '.-') or (len(val) == 2 and val == "-."): return None else: return float(val) else: return val
def getExpand(
self)
def getExpand(self): return self._getContainerProperty('expand')
def getFastStop(
self)
def getFastStop(self): return self._fastStop
def getFg(
self)
def getFg(self): return self._getContainerProperty("fg")
def getFocus(
self)
def getFocus(self): widg = self.topLevel.focus_get() return self.widgetManager.getName(widg)
def getFont(
self)
def getFont(self): return self._getContainerProperty('labelFont').actual()
def getFonts(
self)
def getFonts(self): fonts = list(tkFont.families()) fonts.sort() return fonts
def getFullscreen(
self, title=None)
def getFullscreen(self, title=None): if title is None: container = self._getTopLevel() else: container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) return container.isFullscreen
def getGoogleMapLocation(
self, title)
def getGoogleMapLocation(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["center"]
def getGoogleMapSize(
self, title)
def getGoogleMapSize(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["size"]
def getGoogleMapTerrain(
self, title)
def getGoogleMapTerrain(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["maptype"].title()
def getGoogleMapZoom(
self, title)
def getGoogleMapZoom(self, title): return self.widgetManager.get(WIDGET_NAMES.Map, title).params["zoom"]
def getGridEntries(
self, title)
def getGridEntries(self, title): gui.warn("Deprecated - grids renamed to tables") return self.getTableEntries(title)
def getGridRow(
self, title, rowNumber)
def getGridRow(self, title, rowNumber): return self.getTableRow(title, rowNumber)
def getGridRowCount(
self, title)
def getGridRowCount(self, title): return self.getTableRowCount(title)
def getGridSelectedCells(
self, title)
def getGridSelectedCells(self, title): gui.warn("Deprecated - grids renamed to tables") return self.getTableSelectedCells(title)
def getGuiPadding(
self)
def getGuiPadding(self): return int(str(self.containerStack[0]['container'].cget('padx'))), int(str(self.containerStack[0]['container'].cget('pady')))
def getIcon(
self)
def getIcon(self): container = self._getTopLevel() return container.winIcon
def getImage(
self, name)
def getImage(self, name): label = self.widgetManager.get(WIDGET_NAMES.Image, name) return label.image.path
def getImageDimensions(
self, name)
def getImageDimensions(self, name): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image return img.width(), img.height()
def getImagePath(
self, imagePath)
def getImagePath(self, imagePath): if imagePath is None: return None if self.userImages is not None: imagePath = os.path.join(self.userImages, imagePath) absPath = os.path.abspath(imagePath) return absPath
def getInPadding(
self)
def getInPadding(self): return self._getContainerProperty('ipadx'), self._getContainerProperty('ipady')
def getInputFont(
self)
def getInputFont(self): return self._getContainerProperty('inputFont').actual()
def getLabel(
self, name)
def getLabel(self, name): lab = self.widgetManager.get(WIDGET_NAMES.Label, name) return lab.cget("text")
def getLabelFont(
self)
def getLabelFont(self): return self._getContainerProperty('labelFont').actual()
def getLanguage(
self)
returns the current language
def getLanguage(self): ''' returns the current language ''' return self._language
def getLink(
self, title)
def getLink(self, title): link = self.widgetManager.get(WIDGET_NAMES.Link, title) return link.cget("text")
def getListBox(
self, title)
def getListBox(self, title): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) items = lb.curselection() values = [] for loop in range(len(items)): values.append(lb.get(items[loop])) return values
def getListBoxPos(
self, title)
def getListBoxPos(self, title): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) # bug in tkinter 1.160 returns these as strings items = [int(i) for i in lb.curselection()] return items
def getLocation(
self)
def getLocation(self): container = self._getTopLevel() size, loc = gui.SPLIT_GEOM(container.geometry()) return loc
def getLogFile(
self)
def getLogFile(self): return logging.root.handlers[0].baseFilename
def getLogLevel(
self)
def getLogLevel(self): return logging.getLevelName(logging.getLogger("appJar").getEffectiveLevel())
def getMenuCheckBox(
self, menu, title)
def getMenuCheckBox(self, menu, title): return self._getMenu(menu, title, "cb")
def getMenuRadioButton(
self, menu, title)
def getMenuRadioButton(self, menu, title): return self._getMenu(menu, title, "rb")
def getMessage(
self, title)
def getMessage(self, title): mess = self.widgetManager.get(WIDGET_NAMES.Message, title) return mess.cget("text")
def getMeter(
self, name)
def getMeter(self, name): item = self.widgetManager.get(WIDGET_NAMES.Meter, name) return item.get()
def getOnTop(
self)
def getOnTop(self): return self._getTopLevel().attributes("-topmost") == 1
def getOptionBox(
self, title)
Gets the selected item from the named OptionBox
:param title: the OptionBox to check :returns: the selected item in an OptionBox or a dictionary of all items and their status for a TickOptionBox :raises ItemLookupError: if the title can't be found
def getOptionBox(self, title): """ Gets the selected item from the named OptionBox :param title: the OptionBox to check :returns: the selected item in an OptionBox or a dictionary of all items and their status for a TickOptionBox :raises ItemLookupError: if the title can't be found """ box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) if box.kind == "ticks": val = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) retVal = {} for k, v in val.items(): retVal[k] = bool(v.get()) return retVal else: val = self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) val = val.get().strip() # set to None if it's a divider if val.startswith("-") or len(val) == 0: val = None return val
def getPadding(
self)
def getPadding(self): return self._getContainerProperty('padx'), self._getContainerProperty('pady')
def getPagedWindowPageNumber(
self, title)
def getPagedWindowPageNumber(self, title): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) return pager.getPageNumber()
def getPopUp(
self)
def getPopUp(self): return self.topLevel.POP_UP
def getPreviousFrame(
self, title)
def getPreviousFrame(self, title): return self.widgetManager.get(WIDGET_NAMES.FrameStack, title).getPreviousFrame()
def getProperties(
self, title)
def getProperties(self, title): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) return props.getProperties()
def getProperty(
self, title, prop)
def getProperty(self, title, prop): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) return props.getProperty(prop)
def getRadioButton(
self, title)
def getRadioButton(self, title): var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS) return var.get()
def getRandomColour(
self)
generates a random colour
def getRandomColour(self): """ generates a random colour """ self._loadRandom() de=("%02x"%random.randint(0,255)) re=("%02x"%random.randint(0,255)) we=("%02x"%random.randint(0,255)) return "#"+de+re+we
def getResizable(
self)
def getResizable(self): return self._getTopLevel().isResizable
def getRow(
self)
def getRow(self): return self._getContainerProperty('emptyRow')
def getRowspan(
self)
def getRowspan(self): return self.containerStack[-1]['rowspan']
def getScale(
self, title)
def getScale(self, title): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) return sc.get()
def getSetting(
self, name, default=None)
gets a setting form the settings file
def getSetting(self, name, default=None): """ gets a setting form the settings file """ try: return self.externalSettings[name] except: return default
def getSize(
self)
def getSize(self): container = self._getTopLevel() size, loc = gui.SPLIT_GEOM(container.geometry()) return size
def getSpinBox(
self, title)
def getSpinBox(self, title): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) return spin.get()
def getStatusFont(
self)
def getStatusFont(self): return self._statusFont.actual()
def getSticky(
self)
def getSticky(self): return self._getContainerProperty('sticky')
def getStretch(
self)
def getStretch(self): return self.getExpand()
def getTabbedFrameSelectedTab(
self, title)
def getTabbedFrameSelectedTab(self, title): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) return nb.getSelectedTab()
def getTableEntries(
self, title)
def getTableEntries(self, title): return self.widgetManager.get(WIDGET_NAMES.Table, title).getEntries()
def getTableLastChange(
self, title)
def getTableLastChange(self, title): return self.widgetManager.get(WIDGET_NAMES.Table, title).getLastChange()
def getTableRow(
self, title, rowNumber)
def getTableRow(self, title, rowNumber): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) return grid.getRow(rowNumber)
def getTableRowCount(
self, title)
def getTableRowCount(self, title): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) return grid.getRowCount()
def getTableSelectedCells(
self, title)
def getTableSelectedCells(self, title): return self.widgetManager.get(WIDGET_NAMES.Table, title).getSelectedCells()
def getTextArea(
self, title)
Gets the text in the specified TextArea
:param title: the TextArea to check :returns: the text in the specified TextArea :raises ItemLookupError: if the title can't be found
def getTextArea(self, title): """ Gets the text in the specified TextArea :param title: the TextArea to check :returns: the text in the specified TextArea :raises ItemLookupError: if the title can't be found """ return self.widgetManager.get(WIDGET_NAMES.TextArea, title).getText()
def getTextAreaTag(
self, title, tag)
returns all details about the specified tag
def getTextAreaTag(self, title, tag): """ returns all details about the specified tag """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) return ta.tag_config(tag)
def getTextAreaTags(
self, title)
returns a list of all tags in the text area
def getTextAreaTags(self, title): """ returns a list of all tags in the text area """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) return ta.tag_names()
def getTitle(
self)
def getTitle(self): return self._getTopLevel().title()
def getToggleFrameState(
self, title)
def getToggleFrameState(self, title): toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title) return toggle.isShowing()
def getTransparency(
self)
def getTransparency(self): return self._getTopLevel().attributes("-alpha") * 100
def getTreeSelected(
self, title)
def getTreeSelected(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) return tree.getSelectedText()
def getTreeSelectedXML(
self, title)
def getTreeSelectedXML(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) item = tree.getSelected() if item is not None: return item.node.toxml() else: return None
def getTreeXML(
self, title)
def getTreeXML(self, title): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) return tree.item.node.toxml()
def getTtkTheme(
self)
def getTtkTheme(self): return self.ttkStyle.theme_use()
def getTtkThemes(
self, loadThemes=False)
def getTtkThemes(self, loadThemes=False): if loadThemes: self._loadTtkThemes() if not ThemedStyle: self.error("Custom ttkThemes not available") return self.ttkStyle.theme_names()
def getTurtle(
self, title)
def getTurtle(self, title): return self.widgetManager.get(WIDGET_NAMES.Turtle, title).turtle
def getTurtleScreen(
self, title)
def getTurtleScreen(self, title): return self.widgetManager.get(WIDGET_NAMES.Turtle, title).screen
def getVisible(
self)
def getVisible(self): return self.topLevel.displayed
def getWidget(
self, kind, name, val=None)
def getWidget(self, kind, name, val=None): # if val is set (RadioButtons) - append it if val is not None: name+= "-" + val return self.widgetManager.get(kind, name)
def getWidgetProperty(
self, kind, name, val, prop)
def getWidgetProperty(self, kind, name, val, prop): return self.getWidget(kind, name, val).cget(prop)
def go(
self, language=None, startWindow=None)
Most important function! starts the GUI
def go(self, language=None, startWindow=None): """ Most important function! starts the GUI """ # check if we have a command line language if self._language is not None: language = self._language # if language is populated, we are in internationalisation mode # call the changeLanguage function - to re-badge all the widgets if language is not None: self.changeLanguage(language) if self.splashConfig is not None: gui.trace("SPLASH: %s", self.splashConfig) splash = SplashScreen( self.topLevel, self.splashConfig['text'], self.splashConfig['fill'], self.splashConfig['stripe'], self.splashConfig['fg'], self.splashConfig['font'] ) self.topLevel.withdraw() self._bringToFront(splash) # check the containers have all been stopped if len(self.containerStack) > 1: for i in range(len(self.containerStack) - 1, 0, -1): kind = self.containerStack[i]['type'] if kind != WIDGET_NAMES.Pane: self.warn("No stopContainer called on: %s", WIDGET_NAMES.name(kind)) # update any trees for k in self.widgetManager.group(WIDGET_NAMES.Tree): self.generateTree(k) # create appJar menu, if no menuBar created if not self.hasMenu: self.addAppJarMenu() if self.platform == self.WINDOWS: self.menuBar.add_cascade(menu=self.widgetManager.get(WIDGET_NAMES.Menu, "WIN_SYS")) self.topLevel.config(menu=self.menuBar) if startWindow is not None: self.startWindow = startWindow gui.trace("startWindow parameter: %s", startWindow) # pack it all in & make sure it's drawn self.appWindow.pack(fill=BOTH) if self.useSettings: self.loadSettings(self.settingsFile) self.topLevel.update_idletasks() # check geom is set and set a minimum size, also positions the window if necessary if not self.topLevel.locationSet: self.setLocation('CENTER') if not hasattr(self.topLevel, 'ms'): self.setMinSize() if self.splashConfig is not None: time.sleep(3) splash.destroy() # user hasn't specified anything if self.startWindow is None: if not self.topLevel.displayed: gui.trace("topLevel has been manually hidden - not showing in go()") else: gui.trace("Showing topLevel") self._bringToFront() self.topLevel.deiconify() else: gui.trace("hiding main window") self.hide() sw = self.widgetManager.get(WIDGET_NAMES.SubWindow, startWindow) if sw.blocking: raise Exception("Unable to start appjar with a blocking subWindow") self.showSubWindow(startWindow) # required to make the gui reopen after minimising if self.GET_PLATFORM() == self.MAC:self.topLevel.createcommand('tk::mac::ReopenApplication', self._macReveal) # start the call back & flash loops self._poll() self._flash() # register start-up function if self.topLevel.startFunction is not None: self.topLevel.after_idle(self.topLevel.startFunction) # start the main loop try: self.topLevel.mainloop() except(KeyboardInterrupt, SystemExit) as e: gui.trace("appJar stopped through ^c or exit()") self.stop() except Exception as e: self.exception(e) self.stop()
def gr(
self)
def gr(self): return self.getRow()
def grip(
self, *args, **kwargs)
simpleGUI - adds grip
def grip(self, *args, **kwargs): """ simpleGUI - adds grip """ kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) return self.addGrip(*args, **kwargs)
def growImage(
self, name, x, y='')
def growImage(self, name, x, y=''): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = label.image.zoom(x, y) label.config(image=image) label.config(anchor=CENTER, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: label.config(background=self._getContainerBg()) label.config(width=image.width(), height=image.height()) label.modImage = image # keep a reference!
def hasImageChanged(
self, originalImage, newImage)
def hasImageChanged(self, originalImage, newImage): newAbsImage = self.getImagePath(newImage) if originalImage is None: return True # filename has changed if originalImage.path != newAbsImage: return True # modification time has changed if originalImage.modTime != os.path.getmtime(newAbsImage): return True # no changes return False
def hide(
self, btn=None)
def hide(self, btn=None): self._getTopLevel().displayed = False self._getTopLevel().withdraw()
def hideAllSubWindows(
self, useStopFunction=False)
def hideAllSubWindows(self, useStopFunction=False): for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow): self.hideSubWindow(sub, useStopFunction)
def hideSubWindow(
self, title, useStopFunction=False)
def hideSubWindow(self, title, useStopFunction=False): self.widgetManager.get(WIDGET_NAMES.SubWindow, title).hide(useStopFunction)
def hideTabbedFrameTab(
self, title, tab)
def hideTabbedFrameTab(self, title, tab): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.hideTab(tab)
def hideTitleBar(
self)
def hideTitleBar(self): self.hasTitleBar = False self._doTitleBar()
def hideToolbar(
self)
def hideToolbar(self): self.tb.hide()
def hideWidgetType(
self, kind, name, collapse=False)
def hideWidgetType(self, kind, name, collapse=False): items = self._getWidgetList(kind, name, limit=False) for item in items: if self._widgetHasContainer(kind, item): gui.trace("Hiding widget in container: %s", name) widget = item.master if hasattr(widget, "inContainer") and widget.inContainer: gui.trace("Have container in container") widget = widget.master try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = True except: pass else: gui.trace("Hiding widget: %s", name) if kind in [WIDGET_NAMES.RadioButton]: for rb in item: if rb.text == name: widget = rb widget = item if "in" in widget.grid_info(): gui.trace("Widget hidden: %s", name) info = widget.grid_info() widget.grid_remove() if collapse: widget.master.grid_rowconfigure(info["row"], minsize=0, weight=0) else: gui.trace("Hiding failed - %s not showing", name)
def highlightTextArea(
self, title, start, end='end')
selects text in the specified range
def highlightTextArea(self, title, start, end=END): """ selects text in the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_add(SEL, start, end)
def image(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets images all in one go
def image(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets images all in one go """ widgKind = WIDGET_NAMES.Image kind = kwargs.pop("kind", "standard").lower().strip() speed = kwargs.pop("speed", None) drop = kwargs.pop("drop", None) over = kwargs.pop("over", None) submit = kwargs.pop("submit", None) _map = kwargs.pop("map", None) try: self.widgetManager.verify(widgKind, title) except: # already exists if value is not None: if kind == "data": self.setImageData(title, value, **kwargs) elif kind == "icon": gui.warn("Changing image icons not yet supported: %s.", title) else: self.setImage(title, value) image = self.getImage(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "icon": image = self.addIcon(title, value, *args, **kwargs) elif kind == "data": image = self.addImageData(title, value, *args, **kwargs) else: image = self.addImage(title, value, *args, **kwargs) if speed is not None: self.setAnimationSpeed(title, speed) if over is not None: self.setImageMouseOver(title, over) if submit is not None: if _map is not None: self.setImageMap(title, submit, _map) else: self.setImageSubmitFunction(title, submit) elif submit is None and _map is not None: gui.warn("Must specify a submit function when setting an image map: %s", title) if drop is not None: self.setImageDropTarget(title, drop) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return image
def increaseButtonFont(
self)
def increaseButtonFont(self): self.setButtonFont(size=self._buttonFont['size'] + 1)
def increaseFont(
self)
def increaseFont(self): self.increaseLabelFont() self.increaseButtonFont()
def increaseLabelFont(
self)
def increaseLabelFont(self): self.setLabelFont(size=self._labelFont['size'] + 1)
def info(
message, *args)
wrapper for logMessage - setting level to INFO
@staticmethod def info(message, *args): """ wrapper for logMessage - setting level to INFO """ gui.logMessage(message, "INFO", *args)
def infoBox(
self, title, message, parent=None)
def infoBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: MessageBox.showinfo(title, message) if self.topLevel.displayed: self._bringToFront() else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} MessageBox.showinfo(title, message, **opts) self._bringToFront(parent)
def integerBox(
self, title, message, parent=None)
def integerBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return SimpleDialog.askinteger(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return SimpleDialog.askinteger(title=title, message=message, **opts)
def label(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets labels all in one go
def label(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets labels all in one go """ widgKind = WIDGET_NAMES.Label kind = kwargs.pop("kind", "standard").lower().strip() try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: self.setLabel(title, value) label = self.getLabel(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "flash": label = self._labelMaker(title, value, kind, *args, **kwargs) elif kind == "selectable": label = self._labelMaker(title, value, kind, *args, **kwargs) else: label = self._labelMaker(title, value, "label", *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return label
def labelFrame(
*args, **kwds)
@contextmanager def labelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, **kwargs): name = kwargs.pop("label", kwargs.pop("name", None)) labelFg = kwargs.pop("labelFg", self.fg) try: lf = self.startLabelFrame(title, row, column, colspan, rowspan, sticky, hideTitle, name) except ItemLookupError: lf = self.openLabelFrame(title) self.configure(**kwargs) if not self.ttkFlag: lf.config(fg=labelFg) try: yield lf finally: self.stopLabelFrame()
def lastFrame(
self, title, callFunction=True)
def lastFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showLastFrame(callFunction)
def link(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets links all in one go
def link(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets links all in one go """ widgKind = WIDGET_NAMES.Link try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: self.setLink(title, value) link = self.getLink(title) else: # new widget if value is None: gui.warn("Can't create link: %s, with no value", title) return None kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) link = self._linkMaker(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return link
def listBox(
self, title, value=None, *args, **kwargs)
simpleGUI -- adds, sets & gets listBoxes all in one go
def listBox(self, title, value=None, *args, **kwargs): """ simpleGUI -- adds, sets & gets listBoxes all in one go """ widgKind = WIDGET_NAMES.ListBox rows = kwargs.pop("rows", None) multi = kwargs.pop("multi", False) group = kwargs.pop("group", False) selected = kwargs.pop("selected", None) first = kwargs.pop("first", False) callFunction = kwargs.pop("callFunction", True) # select=select, deselect=??, toggle=??, clear=??, rename=set, replace=update, delete=remove if value is None: mode = 'get' else: mode = 'select' mode = kwargs.pop("mode", mode) try: self.widgetManager.verify(widgKind, title) except: # widget exists if mode == "select": if value is not None: if isinstance(value, int): self.selectListItemAtPos(title, value, *args, **kwargs) else: self.selectListItem(title, value, *args, **kwargs) else: gui.error("No item specified to select in listbox: %s", title) elif mode == "deselect": if value is not None: if isinstance(value, int): self.deselectListItemAtPos(title, value, *args, **kwargs) else: self.deselectListItem(title, value, *args, **kwargs) else: gui.error("No item specified to deselect in listbox: %s", title) elif mode == "toggle": gui.error("%s not implemented yet in listbox: %s", mode, title) elif mode == "clear": self.deselectAllListItems(title) elif mode == "rename": gui.error("%s not implemented yet in listbox: %s", mode, title) elif mode == "replace": if value is not None: self.updateListBox(title, items=value, callFunction=callFunction) else: gui.error("No values specified to replace in listbox: %s", title) elif mode == "delete": if value is not None: if isinstance(value, int): self.removeListItemAtPos(title, value) else: self.removeListItem(title, value) else: gui.error("No value specified to delete in listbox: %s", title) elif mode == "add": if value is not None: select = True if selected is None else selected if type(value) in (list, tuple): self.addListItems(title, items=value, select=select) else: self.addListItem(title, item=value, select=select) else: gui.error("No value specified to add in listbox: %s", title) elif mode == "get": pass else: gui.error("Invalid mode (%s) specified in listbox: %s", mode, title) listBox = self.getListBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) listBox = self._listBoxMaker(title, value, *args, **kwargs) if rows is not None: self.setListBoxRows(title, rows) if multi: self.setListBoxMulti(title) if group: self.setListBoxGroup(title) if selected is not None: self.selectListItemAtPos(title, selected, callFunction=False) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return listBox
def listbox(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for listBox()
def listbox(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for listBox() """ return self.listBox(title, value, *args, **kwargs)
def loadSettings(
self, fileName='appJar.ini', useSettings=True)
loads setting from a settings file, and adjusts the GUI to match called by go() function, if user has requested settings
def loadSettings(self, fileName="appJar.ini", useSettings=True): """ loads setting from a settings file, and adjusts the GUI to match called by go() function, if user has requested settings """ self._loadConfigParser() if not ConfigParser: self.error("Unable to save config file - no configparser") return self.useSettings = useSettings settings = ConfigParser() settings.optionxform = str settings.read(fileName) if settings.has_option("GEOM", "geometry"): geom = settings.get("GEOM", "geometry") if not self.topLevel.ignoreSettings: size, loc = gui.SPLIT_GEOM(geom) gui.trace("Setting topLevel geom: %s as size: %s, loc: %s", geom, size, loc) if size[0] > 1: self.setSize(*size) if loc[0] != -1: self.setLocation(*loc) else: gui.trace("Ignoring topLevel geom: %s", geom) # not finished if settings.has_option("GEOM", "fullscreen"): fs = settings.getboolean('GEOM', "fullscreen") gui.trace("Set fullscreen to: %s", fs) if fs: self.setFullscreen() else: self.exitFullscreen() if settings.has_option("GEOM", "minsize"): self.topLevel.ms = settings.get('GEOM', "minsize").split(",") self._getTopLevel().minsize(self.topLevel.ms[0], self.topLevel.ms[1]) gui.trace("Set minsize to: %s", self.topLevel.ms) if settings.has_option("GEOM", "state"): state = settings.get('GEOM', "state") if state in ["withdrawn", "zoomed"]: self._getTopLevel().state(state) if settings.has_option("TOOLBAR", "pinned") and self.tb.inUse: tb = settings.getboolean("TOOLBAR", "pinned") self.setToolbarPinned(tb) gui.trace("Set toolbar to: %s", tb) if "TOGGLES" in settings.sections(): for k in settings.options("TOGGLES"): try: if self.getToggleFrameState(k) != settings.getboolean("TOGGLES", k): self.toggleToggleFrame(k) except ItemLookupError: gui.error("Settings error, invalid TOGGLES name: %s - discarding", k) if "TABS" in settings.sections(): for k in settings.options("TABS"): try: self.setTabbedFrameSelectedTab(k, settings.get("TABS", k)) except ItemLookupError: gui.error("Settings error, invalid TABS name: %s - discarding", k) if "PAGES" in settings.sections(): for k in settings.options("PAGES"): try: self.setPagedWindowPage(k, settings.getint("PAGES", k)) except ItemLookupError: gui.error("Settings error, invalid PAGES name: %s - discarding", k) if "SUBWINDOWS" in settings.sections(): for k in settings.options("SUBWINDOWS"): if settings.getboolean("SUBWINDOWS", k): gui.trace("Loading settings for %s", k) try: tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, k) # process the geom settings if settings.has_option(k, "geometry"): geom = settings.get(k, "geometry") size, loc = gui.SPLIT_GEOM(geom) if size[0] > 1: gui.trace("Setting size: %s", size) tl.geometry("%sx%s" % (size[0], size[1])) tl.shown = True else: gui.trace("Skipping size: %s", size) if loc[0] > -1: gui.trace("Setting location: %s", loc) self.setSubWindowLocation(k, *loc) else: gui.trace("Skipping location: %s", loc) else: gui.trace("No location found") if settings.has_option(k, "minsize"): ms = settings.get(k, "minsize").split(",") self.setMinSize(tl, ms) # set the state - if there' no startWindow if self.startWindow is None: try: tl.state(settings.get(k, "state")) gui.trace("Set state=%s", tl.state()) except: pass # no state found except ItemLookupError: gui.error("Settings error, invalid SUBWINDOWS name: %s - discarding.", k) else: gui.trace("Skipping settings for %s", k) if "EXTERNAL" in settings.sections(): for k in settings.options("EXTERNAL"): self.externalSettings[k] = settings.get("EXTERNAL", k)
def logMessage(
msg, level, *args)
allows user to log a message - provide a message and a log level any %s tags in the message will be replaced by the relevant positional *args
@staticmethod def logMessage(msg, level, *args): """ allows user to log a message - provide a message and a log level any %s tags in the message will be replaced by the relevant positional *args """ frames = inspect.stack() # try to ensure we only log extras if we're called from above functions if frames[1][3] in ("exception", "critical", "error", "warn", "debug", "trace", "info"): callFrame = "" try: progName = gui.exe_file for s in frames: if progName in s[1]: callFrame = s break except: pass if callFrame != "": callFrame = "Line " + str(callFrame[2]) # user generated call if "appjar.py" not in frames[2][1] or frames[2][3] == "handlerFunction": if callFrame != "": msg = "[" + callFrame + "]: "+str(msg) # appJar logging else: if callFrame != "": msg = "["+callFrame + "->" + str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg) else: msg = "["+str(frames[2][2]) +"/"+str(frames[2][3])+"]: "+str(msg) logger = logging.getLogger("appJar") level = level.upper() if level == "EXCEPTION": logger.exception(msg, *args) elif level == "CRITICAL": logger.critical(msg, *args) elif level == "ERROR": logger.error(msg, *args) elif level == "WARNING": logger.warning(msg, *args) elif level == "INFO": logger.info(msg, *args) elif level == "DEBUG": logger.debug(msg, *args) elif level == "TRACE": logger.trace(msg, *args)
def logTextArea(
self, title)
Creates an md5 hash - can be used later to check if the TextArea has changed The hash is stored in the widget
:param title: the TextArea to hash :returns: None :raises ItemLookupError: if the title can't be found
def logTextArea(self, title): """ Creates an md5 hash - can be used later to check if the TextArea has changed The hash is stored in the widget :param title: the TextArea to hash :returns: None :raises ItemLookupError: if the title can't be found """ self._loadHashlib() if hashlib is False: self.warn("Unable to log TextArea, hashlib library not available") else: text = self.widgetManager.get(WIDGET_NAMES.TextArea, title) text.__hash = text.getTextAreaHash()
def loopSound(
self, sound)
def loopSound(self, sound): self._soundWrap(sound, True, True)
def makeListBoxContainer(
self)
def makeListBoxContainer(self): ParentBox = self._makeParentBox() class ListBoxContainer(Frame, object): def __init__(self, parent, **opts): super(ListBoxContainer, self).__init__(parent) # customised config setters def config(self, cnf=None, **kw): self.configure(cnf, **kw) def configure(self, cnf=None, **kw): # properties to propagate to CheckBoxes kw = gui.CLEAN_CONFIG_DICTIONARY(**kw) # propagate anything left super(ListBoxContainer, self).config(cnf, **kw) return ListBoxContainer
def map(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets maps all in one go
def map(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets maps all in one go """ widgKind = WIDGET_NAMES.Map zoom = kwargs.pop("zoom", None) size = kwargs.pop("size", None) terrain = kwargs.pop("terrain", None) proxy = kwargs.pop("proxy", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists gMap = self.getLabel(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) gMap = self.addGoogleMap(title, *args, **kwargs) if value is not None: self.setGoogleMapLocation(title, value) if zoom is not None: self.setGoogleMapZoom(title, zoom) if size is not None: self.setGoogleMapSize(title, size) if terrain is not None: self.setGoogleMapTerrain(title, terrain) if proxy is not None: self.setGoogleMapProxy(title, proxy) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return gMap
def message(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets messages all in one go
def message(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets messages all in one go """ widgKind = WIDGET_NAMES.Message try: self.widgetManager.verify(WIDGET_NAMES.Message, title) except: # widget exists if value is not None: self.setMessage(title, value) msg = self.getMessage(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) msg = self._messageMaker(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return msg
def meter(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets meters all in one go
def meter(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets meters all in one go """ widgKind = WIDGET_NAMES.Meter kind = kwargs.pop("kind","'meter") fill = kwargs.pop("fill", None) text = kwargs.pop("text", None) try: self.widgetManager.verify(WIDGET_NAMES.Meter, title) except: # widget exists meter = self.getMeter(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "split": meter = self._addMeter(title, "SPLIT", **kwargs) elif kind == "dual": meter = self._addMeter(title, "DUAL", **kwargs) else: meter = self._addMeter(title, "METER", **kwargs) if value is not None: self.setMeter(title, value, text=text) if fill is not None: self.setMeterFill(title, fill) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return meter
def microbit(
self, title, *args, **kwargs)
simpleGUI - adds, sets & gets microbits all in one go
def microbit(self, title, *args, **kwargs): '''simpleGUI - adds, sets & gets microbits all in one go''' widgKind = WIDGET_NAMES.MicroBit image = kwargs.pop("image", None) brightness = kwargs.pop("brightness", None) x = kwargs.pop("x", None) y = kwargs.pop("y", None) clear = kwargs.pop("clear", False) try: self.widgetManager.verify(widgKind, title) except: # widget exists mb = self.getMicroBit(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) mb = self.addMicroBit(title, *args, **kwargs) if image is not None: self.setMicroBitImage(title, image) if brightness is not None: self.setMicroBitPixel(title, x, y, brightness) if clear: self.clearMicroBit(title) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return mb
def moveWidgetType(
self, kind, name, row=None, column=0, colspan=0, rowspan=0, sticky='we')
def moveWidgetType(self, kind, name, row=None, column=0, colspan=0, rowspan=0, sticky=W+E): self.hideWidgetType(kind, name, collapse=True) widget = self.widgetManager.get(kind, name) container = self.getContainer() if container != widget.master: widget = self._cloneWidget(widget, container) self.widgetManager.update(kind, name, widget) self._positionWidget(widget, row, column, colspan, rowspan, sticky, updateBg=False) return widget
def nextFrame(
self, title, callFunction=True)
def nextFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showNextFrame(callFunction)
def note(
*args, **kwds)
@contextmanager def note(self, title, tabTitle=None, **kwargs): if tabTitle is None: note = self.startNote(title) else: note = self.openNote(title, tabTitle) self.configure(**kwargs) try: yield note finally: self.stopNote()
def notebook(
*args, **kwds)
@contextmanager def notebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): try: note = self.startNotebook(title, row, column, colspan, rowspan, sticky) except ItemLookupError: note = self.openNotebook(title) self.configure(**kwargs) try: yield note finally: self.stopNotebook()
def numBox(
self, title='Number Box', question='Enter a number', parent=None)
def numBox(self, title="Number Box", question="Enter a number", parent=None): self.topLevel.update_idletasks() if parent is None: parent = self.topLevel else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) return NumDialog(parent, title, question).result
def numberBox(
self, title='Number Box', question='Enter a number', parent=None)
def numberBox(self, title="Number Box", question="Enter a number", parent=None): return self.numBox(title, question, parent)
def okBox(
self, title, message, parent=None)
def okBox(self, title, message, parent=None): self.topLevel.update_idletasks() title, message = self._translatePopup(title, message) if parent is None: return MessageBox.askokcancel(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return MessageBox.askokcancel(title, message, **opts)
def openBox(
self, title=None, dirName=None, fileTypes=None, asFile=False, parent=None, multiple=False, mode='r')
def openBox(self, title=None, dirName=None, fileTypes=None, asFile=False, parent=None, multiple=False, mode='r'): self.topLevel.update_idletasks() # define options for opening options = {} if title is not None: options['title'] = title if dirName is not None: options['initialdir'] = dirName if fileTypes is not None: options['filetypes'] = fileTypes if parent is not None: options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) if asFile: options["mode"] = mode if multiple: files = list(filedialog.askopenfiles(**options)) else: files = filedialog.askopenfile(**options) return files # will return "" if cancelled else: if multiple: files = list(self.topLevel.tk.splitlist(filedialog.askopenfilenames(**options))) else: files = filedialog.askopenfilename(**options) return files
def openFrame(
self, title)
def openFrame(self, title): try: return self._openContainer(WIDGET_NAMES.Frame, title) except: return self._openContainer(WIDGET_NAMES.SubFrame, title)
def openFrameStack(
self, title)
def openFrameStack(self, title): return self._openContainer(WIDGET_NAMES.FrameStack, title)
def openLabelFrame(
self, title)
def openLabelFrame(self, title): return self._openContainer(WIDGET_NAMES.LabelFrame, title)
def openNote(
self, frameTitle, tabTitle)
def openNote(self, frameTitle, tabTitle): return self._openContainer(WIDGET_NAMES.Notebook, frameTitle+"__"+tabTitle)
def openNotebook(
self, title)
def openNotebook(self, title): return self._openContainer(WIDGET_NAMES.Notebook, title)
def openPage(
self, windowTitle, pageNumber)
def openPage(self, windowTitle, pageNumber): return self._openContainer(WIDGET_NAMES.Page, windowTitle+"__"+str(pageNumber))
def openPagedWindow(
self, title)
def openPagedWindow(self, title): return self._openContainer(WIDGET_NAMES.PagedWindow, title)
def openPane(
self, title)
def openPane(self, title): return self._openContainer(WIDGET_NAMES.Pane, title)
def openPanedFrame(
self, title)
def openPanedFrame(self, title): return self._openContainer(WIDGET_NAMES.PanedFrame, title)
def openRootPage(
self, title)
def openRootPage(self, title): return self._openContainer(WIDGET_NAMES.RootPage, title)
def openScrollPane(
self, title)
def openScrollPane(self, title): return self._openContainer(WIDGET_NAMES.ScrollPane, title)
def openSubFrame(
self, frameTitle, frameNumber)
def openSubFrame(self, frameTitle, frameNumber): return self._openContainer(WIDGET_NAMES.SubFrame, frameTitle+"__"+str(frameNumber))
def openSubWindow(
self, title)
def openSubWindow(self, title): return self._openContainer(WIDGET_NAMES.SubWindow, title)
def openTab(
self, frameTitle, tabTitle)
def openTab(self, frameTitle, tabTitle): return self._openContainer(WIDGET_NAMES.Tab, frameTitle+"__"+tabTitle)
def openTabbedFrame(
self, title)
def openTabbedFrame(self, title): return self._openContainer(WIDGET_NAMES.TabbedFrame, title)
def openToggleFrame(
self, title)
def openToggleFrame(self, title): return self._openContainer(WIDGET_NAMES.ToggleFrame, title)
def option(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for optionBox()
def option(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for optionBox() """ return self.optionBox(title, value, *args, **kwargs)
def optionBox(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets optionBoxes all in one go
def optionBox(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets optionBoxes all in one go """ widgKind = WIDGET_NAMES.OptionBox kind = kwargs.pop("kind", "standard").lower().strip() label = kwargs.pop("label", False) callFunction = kwargs.pop("callFunction", True) override = kwargs.pop("override", False) checked = kwargs.pop("checked", True) selected = kwargs.pop("selected", None) disabled = kwargs.pop("disabled", "-") # select=set, replace=change, rename=rename, clear=clear, delete=delete if value is None: mode = 'get' else: mode = 'select' mode = kwargs.pop("mode", mode) index = kwargs.pop("index", None) newName = kwargs.pop("newName", None) try: self.widgetManager.verify(WIDGET_NAMES.OptionBox, title) except: # widget exists if mode == "select": if value is not None: self.setOptionBox(title, index=value, value=True, callFunction=callFunction, override=override) else: gui.error("No item specified to select in optionBox: %s", title) elif mode == "deselect": if value is not None: self.setOptionBox(title, index=value, value=False, callFunction=callFunction, override=override) else: self.clearOptionBox(title, callFunction=callFunction) gui.info("optionBox set back to its original state: %s", title) elif mode == "toggle": gui.error("Toggling optionboxes not supported: %s", title) elif mode == "clear": if value is not None: gui.error("No value should be specified wen clearing optionBox: %s", title) else: self.clearOptionBox(title, callFunction=callFunction) elif mode == "rename": if value is not None: self.renameOptionBox(title, item=value, newName=newName, callFunction=callFunction) else: gui.error("No item specified to rename in optionBox: %s", title) elif mode == "replace": if value is not None: self.changeOptionBox(title, options=value, index=index, callFunction=callFunction) else: gui.error("No values specified to replace in optionBox: %s", title) elif mode == "delete": if value is not None: self.deleteOptionBox(title, index=value) else: gui.error("No item specified to delete in optionBox: %s", title) elif mode == "get": pass else: gui.error("Invalid mode (%s) specified in optionBox: %s", mode, title) opt = self.getOptionBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == "ticks": if label: opt = self.addLabelTickOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs) else: opt = self.addTickOptionBox(title, value, *args, disabled=disabled, **kwargs) else: if label: opt = self.addLabelOptionBox(title, value, *args, label=label, disabled=disabled, **kwargs) else: opt = self.addOptionBox(title, value, *args, disabled=disabled, **kwargs) if selected is not None: self.setOptionBox(title, selected) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return opt
def optionbox(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for optionBox()
def optionbox(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for optionBox() """ return self.optionBox(title, value, *args, **kwargs)
def page(
*args, **kwds)
@contextmanager def page(self, windowTitle=None, pageNumber=None, sticky="nw", **kwargs): if windowTitle is None: pg = self.startPage(sticky) else: pg = self.openPage(windowTitle, pageNumber) self.configure(**kwargs) try: yield pg finally: self.stopPage()
def pagedWindow(
*args, **kwds)
@contextmanager def pagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs): try: pw = self.startPagedWindow(title, row, column, colspan, rowspan) except ItemLookupError: pw = self.openPagedWindow(title) self.configure(**kwargs) try: yield pw finally: self.stopPagedWindow()
def panedFrame(
*args, **kwds)
@contextmanager def panedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): vertical = kwargs.pop("vertical", False) sash = kwargs.pop("sash", 50) reOpen = False try: pane = self.startPanedFrame(title, row, column, colspan, rowspan, sticky) except ItemLookupError: reOpen = True pane = self.openPane(title) if vertical: self.setPanedFrameVertical(title) self.configure(**kwargs) try: yield pane finally: if reOpen: self.stopContainer() else: self.stopPanedFrame() self.setPaneSashPosition(sash, pane)
def panedFrameVertical(
*args, **kwds)
@contextmanager def panedFrameVertical(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): gui.warn('Setting panedFrameVertical(%s) is deprecated, please use panedFrame(vertical=True)', title) reOpen = False sash = kwargs.pop("sash", 50) try: pane = self.startPanedFrameVertical(title, row, column, colspan, rowspan, sticky) except ItemLookupError: reOpen = True pane = self.openPane(title) self.configure(**kwargs) try: yield pane finally: if reOpen: self.stopContainer() else: self.stopPanedFrame() self.setPaneSashPosition(sash, pane)
def pie(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets pies all in one go
def pie(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets pies all in one go """ widgKind = WIDGET_NAMES.PieChart name = kwargs.pop("name", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists if name is not None: self.setPieChart(title, name, value) pie = self.getPieChart(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) pie = self.addPieChart(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return pie
def playNote(
self, note, duration=200)
def playNote(self, note, duration=200): self._loadWinsound() if self.platform == self.WINDOWS and winsound is not False: try: if isinstance(note, UNIVERSAL_STRING): freq = self.NOTES[note.lower()] else: freq = note except KeyError: raise Exception("Error: cannot play note - " + note) try: if isinstance(duration, UNIVERSAL_STRING): length = self.DURATIONS[duration.upper()] else: length = duration except KeyError: raise Exception("Error: cannot play duration - " + duration) try: winsound.Beep(freq, length) except RuntimeError: raise Exception( "Sound not available on this platform: " + platform()) else: # sound not available at this time raise Exception( "Sound not supported on this platform: " + platform())
def playSound(
self, sound, wait=False)
def playSound(self, sound, wait=False): self._soundWrap(sound, True, False, wait)
def plot(
self, title, t=None, s=None, *args, **kwargs)
simpleGUI - adds, sets & gets plots all in one go
def plot(self, title, t=None, s=None, *args, **kwargs): """ simpleGUI - adds, sets & gets plots all in one go """ widgKind = WIDGET_NAMES.Plot nav = kwargs.pop("nav", kwargs.pop("showNav", False)) try: self.widgetManager.verify(widgKind, title) except: # widget exists keepLabels = kwargs.pop("keepLabels", False) self.updatePlot(title, t, s, keepLabels=keepLabels) plot = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if t is not None: if s is not None: plot = self.addPlot(title, t, s, *args, showNav=nav, **kwargs) else: gui.warn("Invalid parameters for plot: must provide t & s") return None else: plot = self.addPlotFig(title, *args, showNav=nav, **kwargs) return plot
def popUp(
self, title, message=None, kind='info', parent=None)
simpleGUI - shortener for the various popUps
def popUp(self, title, message=None, kind="info", parent=None): """ simpleGUI - shortener for the various popUps """ if message is None: message = title title = kind.capitalize() + " Dialog" if kind == "info": return self.infoBox(title, message, parent) elif kind == "error": return self.errorBox(title, message, parent) elif kind == "warning": return self.warningBox(title, message, parent) elif kind == "yesno": return self.yesNoBox(title, message, parent) elif kind == "question": return self.questionBox(title, message, parent) elif kind == "ok": return self.okBox(title, message, parent) elif kind == "retry": return self.retryBox(title, message, parent) elif kind == "string": return self.stringBox(title, message, parent) elif kind == "integer": return self.integerBox(title, message, parent) elif kind == "float": return self.floatBox(title, message, parent) elif kind == "text": return self.textBox(title, message, parent) elif kind == "number": return self.numberBox(title, message, parent) else: gui.error("Invalid popUp kind: %s, with title: %s", kind, title)
def prevFrame(
self, title, callFunction=True)
def prevFrame(self, title, callFunction=True): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showPrevFrame(callFunction)
def prompt(
self, title, message, kind='string', parent=None)
def prompt(self, title, message, kind="string", parent=None): return self.popUp(title, message, kind, parent)
def properties(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets properties all in one go
def properties(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets properties all in one go """ widgKind = WIDGET_NAMES.Properties try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: ed to work out args... self.setProperty(title, prop=value) props = self.getProperties(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) props = self.addProperties(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return props
def questionBox(
self, title, message, parent=None)
def questionBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return True if MessageBox.askquestion(title, message).lower() == "yes" else False else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return True if MessageBox.askquestion(title, message, **opts).lower() == "yes" else False
def queueFunction(
self, func, *args, **kwargs)
adds the specified function & arguments to the event queue Functions in the event queue are actioned by the gui's main thread
:param func: the function to call :param args: any number of ordered arguments :param *kwargs: any number of named arguments :raises Full: if unable to add the function to the queue
def queueFunction(self, func, *args, **kwargs): """ adds the specified function & arguments to the event queue Functions in the event queue are actioned by the gui's main thread :param func: the function to call :param *args: any number of ordered arguments :param **kwargs: any number of named arguments :raises Full: if unable to add the function to the queue """ self._loadThreading() if Queue is False: gui.warn("Unable to queueFunction - threading not possible.") else: self.eventQueue.put((5, func, args, kwargs), block=False)
def queuePriorityFunction(
self, func, *args, **kwargs)
queues the function with a higher priority - not working yet
def queuePriorityFunction(self, func, *args, **kwargs): """ queues the function with a higher priority - not working yet """ self._loadThreading() if Queue is False: gui.warn("Unable to queueFunction - threading not possible.") else: self.eventQueue.put((1, func, args, kwargs), block=False)
def radio(
self, title, name=None, *args, **kwargs)
simpleGUI - shortner for radioButton()
def radio(self, title, name=None, *args, **kwargs): """ simpleGUI - shortner for radioButton() """ return self.radioButton(title, name, *args, **kwargs)
def radioButton(
self, title, name=None, *args, **kwargs)
simpleGUI - adds, sets & gets radioButtons all in one go
def radioButton(self, title, name=None, *args, **kwargs): """ simpleGUI - adds, sets & gets radioButtons all in one go """ widgKind = WIDGET_NAMES.RadioButton selected = kwargs.pop("selected", False) callFunction = kwargs.pop("callFunction", True) change = kwargs.pop("change", None) kind = kwargs.pop('kind', 'standard') # need slightly different approach, as use two params if name is None: return self.getRadioButton(title) # no name = get else: ident = title + "-" + name try: self.widgetManager.verify(widgKind, ident) except: self.setRadioButton(title, name, callFunction=callFunction) rb = self.getRadioButton(title) selected = False else: kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) rb = self._radioButtonMaker(title, name, *args, **kwargs) if selected: self.setRadioButton(title, name) if change is not None: self.setRadioButtonChangeFunction(title, change) if kind == "square": if self.platform == self.MAC: gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title) elif not self.ttkFlag: rb.config(indicatoron=0) else: gui.warn("Square radiobuttons not available in ttk, for radiobutton %s", title) if len(kwargs) > 0: self._configWidget(ident, widgKind, **kwargs) return rb
def raiseFrame(
self, title)
will bring the named frame in front of any others
def raiseFrame(self, title): ''' will bring the named frame in front of any others ''' gui.trace("Raising frame: %s", title) self.widgetManager.get(WIDGET_NAMES.Frame, title).lift()
def refreshDbGrid(
self, title)
def refreshDbGrid(self, title): gui.warn("Deprecated - grids renamed to tables") return self.refreshDbTable(title)
def refreshDbOptionBox(
self, title, selected=None)
def refreshDbOptionBox(self, title, selected=None): opt = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) data = self._getDbTables(opt.db) self.changeOptionBox(title, data) if selected is not None: self.setOptionBox(title, selected)
def refreshDbTable(
self, title)
def refreshDbTable(self, title): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) self._importSqlite3() if not sqlite3: self.error("Unable to load DB data - can't load sqlite3") return with sqlite3.connect(grid.db) as conn: cursor = conn.cursor() dataQuery = 'SELECT * from ' + grid.dbTable # select all data cursor.execute(dataQuery) self.replaceAllTableRows(title, cursor)
def refreshPlot(
self, title)
def refreshPlot(self, title): canvas = self.widgetManager.get(WIDGET_NAMES.Plot, title) canvas.draw()
def registerEvent(
self, func)
Queue a function, to be executed every poll time
def registerEvent(self, func): """ Queue a function, to be executed every poll time """ self.events.append(func)
def reloadImage(
self, name, imageFile)
def reloadImage(self, name, imageFile): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = self._getImage(imageFile, False) self._populateImage(name, image)
def reloadImageData(
self, name, imageData, fmt='gif')
def reloadImageData(self, name, imageData, fmt="gif"): self.setImageData(name, imageData, fmt)
def removeAllWidgets(
self, current=False, sub=False)
def removeAllWidgets(self, current=False, sub=False): if current: self.emptyCurrentContainer() else: gui.trace('Removing all widgets from appJar') if sub: self.destroyAllSubWindows() containerData = self.containerStack[0] container = containerData['container'] self._emptyContainerObj(container) # reset container values containerData = self._prepContainer(containerData["title"], containerData["type"], containerData["container"], 0, 1) self.containerStack[0] = containerData
def removeAutoEntry(
self, title, value)
def removeAutoEntry(self, title, value): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.removeWord(value) except AttributeError: gui.error("You can only remove items from an AutoEntry, %s is not an AutoEntry.", title)
def removeBgImage(
self)
def removeBgImage(self): self.bgLabel.config(image="") # self.containerStack[0]['container'].config(image=None) # window as a # label doesn't work... # remove the reference - shouldn't be cached self.containerStack[0]['container'].image = None
def removeGoogleMapMarker(
self, title, label)
def removeGoogleMapMarker(self, title, label): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if len(label) == 0: gMap.removeMarkers() else: gMap.removeMarker(label)
def removeListItem(
self, title, item)
def removeListItem(self, title, item): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) positions = self._getListPositions(title, item) if len(positions) > 0: lb.delete(positions[0]) # show & select this item if positions[0] >= lb.size(): positions[0] -= 1 self.selectListItemAtPos(title, positions[0])
def removeListItemAtPos(
self, title, pos)
def removeListItemAtPos(self, title, pos): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) items = lb.get(0, END) if pos >= len(items): raise Exception("Invalid position: " + str(pos) + " must be between 0 and " + str(len(items)-1)) lb.delete(pos) # show & select this item if pos >= lb.size(): pos -= 1 self.selectListItemAtPos(title, pos)
def removeStatusbar(
self)
def removeStatusbar(self): if self.hasStatus: while len(self._statusFields) > 0: self.removeStatusbarField(0) self.statusFrame.pack_forget() self.statusFrame.destroy() self.hasStatus = False self.header = ""
def removeStatusbarField(
self, field)
def removeStatusbarField(self, field): if self.hasStatus and field < len(self._statusFields): self._statusFields[field].pack_forget() self._statusFields[field].destroy() del self._statusFields[field] else: raise ItemLookupError("Invalid field number for statusbar: " + str(field))
def removeToolbar(
self, hide=True)
def removeToolbar(self, hide=True): while len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) > 0: self.removeToolbarButton(list(self.widgetManager.group(WIDGET_NAMES.Toolbar))[0], hide)
def removeToolbarButton(
self, name, hide=True)
def removeToolbarButton(self, name, hide=True): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) self.widgetManager.get(WIDGET_NAMES.Toolbar, name).destroy() self.widgetManager.remove(WIDGET_NAMES.Toolbar, name) if hide: if len(self.widgetManager.group(WIDGET_NAMES.Toolbar)) == 0: self.tb.pack_forget() self.tb.inUse = False if self.tb.toolbarMin is not None: self.tb.toolbarMin.pack_forget()
def removeWidgetType(
self, kind, name, collapse=False)
def removeWidgetType(self, kind, name, collapse=False): if kind == WIDGET_NAMES.RadioButton: gui.error("Can't remove widget %s - %s", kind, name) return item = self.widgetManager.get(kind, name) # if it's a flasher, remove it if item in self.widgetManager.group(WIDGET_NAMES.FlashLabel): gui.trace("Remove flash label: %s", name) self.widgetManager.remove(WIDGET_NAMES.FlashLabel, item) if len(self.widgetManager.group(WIDGET_NAMES.FlashLabel)) == 0: self.doFlash = False # animated images... if self._widgetHasContainer(kind, item): gui.trace("Remove widget (%s) in container: %s", kind, name) parent = item.master # is it a container in a labelBox? # if so - remove & destroy the labelBox if hasattr(parent, "inContainer") and parent.inContainer: gui.trace("Container in container") labParent = parent.master self.widgetManager.remove(WIDGET_NAMES.FrameBox, labParent) self.widgetManager.remove(WIDGET_NAMES.Label, name) self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name) labParent.grid_forget() labParent.destroy() # otherwise destroy this container & a label if we have one else: parent.grid_forget() parent.destroy() try: self.widgetManager.remove(WIDGET_NAMES.Label, name) self.widgetManager.remove(WIDGET_NAMES.FrameLabel, name) except: pass self.widgetManager.remove(WIDGET_NAMES.FrameBox, parent) else: gui.trace("Remove widget: %s", name) item.grid_forget() self.cleanseWidgets(item)
def renameMenu(
self, title, newName)
def renameMenu(self, title, newName): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: self.menuBar.entryconfigure(title, label=newName) except TclError: gui.error("Unable to rename menu: %s - item not found", title)
def renameMenuItem(
self, title, item, newName)
def renameMenuItem(self, title, item, newName): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, title) try: theMenu.entryconfigure(item, label=newName) except TclError: gui.error("Unable to rename menu item: %s, in menu: %s - item not found", item, title)
def renameOptionBoxItem(
self, title, item, newName=None, callFunction=False)
Changes the text of the specified item in the named OptionBox :param title: the OptionBox to change :param item: the item to rename :param newName: the value to rename it with :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found
def renameOptionBoxItem(self, title, item, newName=None, callFunction=False): """ Changes the text of the specified item in the named OptionBox :param title: the OptionBox to change :param item: the item to rename :param newName: the value to rename it with :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ self.widgetManager.check(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS) self.setOptionBox(title, item, value=newName, callFunction=callFunction)
def replaceAllGridRows(
self, title, data)
def replaceAllGridRows(self, title, data): return self.replaceAllTableRows(title, data)
def replaceAllTableRows(
self, title, data, deleteHeader=True)
def replaceAllTableRows(self, title, data, deleteHeader=True): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.deleteAllRows(deleteHeader=deleteHeader) grid.addRows(data, scroll=False)
def replaceDbGrid(
self, title, db, table)
def replaceDbGrid(self, title, db, table): gui.warn("Deprecated - grids renamed to tables") return self.replaceDbTable(title, db, table)
def replaceDbTable(
self, title, db, table)
def replaceDbTable(self, title, db, table): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.db = db grid.dbTable = table self._importSqlite3() if not sqlite3: self.error("Unable to load DB data - can't load sqlite3") return with sqlite3.connect(db) as conn: cursor = conn.cursor() dataQuery = 'SELECT * from ' + table # select all data cursor.execute(dataQuery) self.setTableHeaders(title, cursor) self.replaceAllTableRows(title, cursor) self.topLevel.update_idletasks()
def replaceGridRow(
self, title, rowNum, data)
def replaceGridRow(self, title, rowNum, data): return self.replaceTableRow(title, rowNum, data)
def replaceTableRow(
self, title, rowNum, data)
def replaceTableRow(self, title, rowNum, data): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.replaceRow(rowNum, data)
def resetAllProperties(
self, callFunction=False)
def resetAllProperties(self, callFunction=False): props = {} for k in self.widgetManager.group(WIDGET_NAMES.Properties): self.resetProperties(k, callFunction)
def resetProperties(
self, title, callFunction=True)
def resetProperties(self, title, callFunction=True): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.resetProperties(callFunction)
def resizeBgImage(
self)
def resizeBgImage(self): if self.containerStack[0]['container'].image is None: return else: pass
def retryBox(
self, title, message, parent=None)
def retryBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return MessageBox.askretrycancel(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return MessageBox.askretrycancel(title, message, **opts)
def saveBox(
self, title=None, fileName=None, dirName=None, fileExt='.txt', fileTypes=None, asFile=False, parent=None)
def saveBox( self, title=None, fileName=None, dirName=None, fileExt=".txt", fileTypes=None, asFile=False, parent=None): self.topLevel.update_idletasks() if fileTypes is None: fileTypes = [('all files', '.*'), ('text files', '.txt')] # define options for opening options = {} options['defaultextension'] = fileExt options['filetypes'] = fileTypes options['initialdir'] = dirName options['initialfile'] = fileName options['title'] = title if parent is not None: options["parent"] = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) if asFile: return filedialog.asksaveasfile(mode='w', **options) # will return "" if cancelled else: return filedialog.asksaveasfilename(**options)
def saveGoogleMap(
self, title, fileLocation)
def saveGoogleMap(self, title, fileLocation): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) return gMap.saveTile(fileLocation)
def saveSettings(
self, fileName='appJar.ini')
saves the current settings to a file called automatically by stop() of settings were loaded at start
def saveSettings(self, fileName="appJar.ini"): """ saves the current settings to a file called automatically by stop() of settings were loaded at start """ self._loadConfigParser() if not ConfigParser: self.error("Unable to save config file - no configparser") return settings = ConfigParser() settings.optionxform = str settings.add_section('GEOM') geom = self.topLevel.geometry() ms = self.topLevel.minsize() ms = "%s,%s" % (ms[0], ms[1]) settings.set('GEOM', 'geometry', geom) gui.trace("Save geom as: %s", geom) settings.set('GEOM', 'minsize', ms) settings.set('GEOM', "fullscreen", str(self.topLevel.attributes('-fullscreen'))) settings.set('GEOM', "state", str(self.topLevel.state())) # get toolbar setting if self.tb.inUse: gui.trace("Saving toolbar settings") settings.add_section("TOOLBAR") settings.set("TOOLBAR", "pinned", str(self.tb.pinned)) # get container settings for k, v in self.widgetManager.group(WIDGET_NAMES.ToggleFrame).items(): gui.trace("Saving toggle %s", k) if "TOGGLES" not in settings.sections(): settings.add_section("TOGGLES") settings.set("TOGGLES", k, str(v.isShowing())) for k, v in self.widgetManager.group(WIDGET_NAMES.TabbedFrame).items(): gui.trace("Saving tab %s", k) if "TABS" not in settings.sections(): settings.add_section("TABS") settings.set("TABS", k, str(v.getSelectedTab())) for k, v in self.widgetManager.group(WIDGET_NAMES.PagedWindow).items(): gui.trace("Saving page %s", k) if "PAGES" not in settings.sections(): settings.add_section("PAGES") settings.set("PAGES", k, str(v.getPageNumber())) for k, v in self.widgetManager.group(WIDGET_NAMES.SubWindow).items(): if "SUBWINDOWS" not in settings.sections(): settings.add_section("SUBWINDOWS") if v.shown: v.update() settings.set("SUBWINDOWS", k, "True") settings.add_section(k) settings.set(k, "geometry", v.geometry()) ms = v.minsize() settings.set(k, 'minsize', "%s,%s" % (ms[0], ms[1])) settings.set(k, "state", v.state()) gui.trace("Saving subWindow %s: geom=%s, state=%s, minsize=%s", k, v.geometry(), v.state(), ms) else: settings.set("SUBWINDOWS", k, "False") gui.trace("Skipping subwindow: %s", k) for k, v in self.externalSettings.items(): if "EXTERNAL" not in settings.sections(): settings.add_section("EXTERNAL") settings.set("EXTERNAL", k, str(v)) # pane positions? # sub windows geom & visibility # scrollpane x & y positions # language # ttk # debug level with open(fileName, 'w') as theFile: settings.write(theFile)
def scale(
self, title, *args, **kwargs)
simpleGUI - adds, sets & gets scales all in one go
def scale(self, title, *args, **kwargs): """ simpleGUI - adds, sets & gets scales all in one go """ widgKind = WIDGET_NAMES.Scale vert = kwargs.pop("direction", "horizontal").lower() == "vertical" increment = kwargs.pop("increment", None) value = kwargs.pop("value", None) interval = kwargs.pop("interval", None) show = kwargs.pop("show", False) _range = kwargs.pop("range", None) callFunction = kwargs.pop("callFunction", True) label = kwargs.pop("label", False) try: self.widgetManager.verify(widgKind, title) except: # widget exists scale = self.getScale(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) scale = self._scaleMaker(title, label, *args, **kwargs) if _range is not None: self.setScaleRange(title, _range[0], _range[1]) if vert: self.setScaleVertical(title) if increment is not None: self.setScaleIncrement(title, increment) if interval is not None: self.showScaleIntervals(title, interval) if show: self.showScaleValue(title) if value is not None: self.setScale(title, value, callFunction) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return scale
def scrollPane(
*args, **kwds)
@contextmanager def scrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): disabled = kwargs.pop("disabled", "") try: sp = self.startScrollPane(title, row, column, colspan, rowspan, sticky, disabled) except ItemLookupError: sp = self.openScrollPane(title) self.configure(**kwargs) try: yield sp finally: self.stopScrollPane()
def searchGoogleMap(
self, title, location)
def searchGoogleMap(self, title, location): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) gMap.changeLocation(location)
def searchTextArea(
self, title, pattern, start=None, stop=None, nocase=True, backwards=False)
will find and highlight the specified text, returning the position
def searchTextArea(self, title, pattern, start=None, stop=None, nocase=True, backwards=False): """ will find and highlight the specified text, returning the position """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) if start is None: start = ta.index(INSERT) pos = ta.search(pattern, start, stopindex=stop, nocase=nocase, backwards=backwards) ta.focus_set() if pos == "": return None else: end = str(pos) + " + " + str(len(pattern)) + " c" ta.see(pos) ta.tag_add(SEL, pos, end) ta.mark_set("insert", pos) return pos
def selectFrame(
self, title, num, callFunction=True)
def selectFrame(self, title, num, callFunction=True): if type(num) in (list, tuple): num = num[0] num = int(num) self.widgetManager.get(WIDGET_NAMES.FrameStack, title).showFrame(num, callFunction)
def selectGridColumn(
self, title, col, highlight=None)
def selectGridColumn(self, title, col, highlight=None): return self.selectTableColumn(title, col, highlight)
def selectGridRow(
self, title, row, highlight=None)
def selectGridRow(self, title, row, highlight=None): gui.warn("Deprecated - grids renamed to tables") return self.selectTableRow(title, row, highlight)
def selectListItem(
self, title, item, callFunction=True)
def selectListItem(self, title, item, callFunction=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) positions = self._getListPositions(title, item) if len(positions) > 1 and lb.cget("selectmode") == EXTENDED: allOk = True for pos in positions: if not self.selectListItemAtPos(title, pos, callFunction): allOk = False return allOk elif len(positions) > 1: gui.warn("Unable to select multiple items for list: %s. Selecting first item: %s", title, item[0]) return self.selectListItemAtPos(title, positions[0], callFunction) elif len(positions) == 1: return self.selectListItemAtPos(title, positions[0], callFunction) else: gui.warn("Invalid list item(s): %s for list: %s", item, title) return False
def selectListItemAtPos(
self, title, pos, callFunction=False)
def selectListItemAtPos(self, title, pos, callFunction=False): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if lb.size() == 0: gui.warn("No items in list: %s, unable to select item at pos: %s", title, pos) return False if pos < 0 or pos > lb.size() - 1: gui.warn("Invalid list position: %s for list: %s (max: %s)", pos, title, lb.size()-1) return False # clear previous selection if we're not multi if lb.cget("selectmode") != EXTENDED: lb.selection_clear(0, END) # show & select this item lb.see(pos) lb.activate(pos) lb.selection_set(pos) # now call function if callFunction and hasattr(lb, 'cmd'): lb.cmd() self.topLevel.update_idletasks() return True
def selectTableColumn(
self, title, col, highlight=None)
def selectTableColumn(self, title, col, highlight=None): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.selectColumn(col, highlight)
def selectTableRow(
self, title, row, highlight=None)
def selectTableRow(self, title, row, highlight=None): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.selectRow(row, highlight)
def separator(
self, *args, **kwargs)
simpleGUI - adds horizontal/vertical separators
def separator(self, *args, **kwargs): """ simpleGUI - adds horizontal/vertical separators """ direction = kwargs.pop("direction", "horizontal").lower() kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if direction == "vertical": return self.addVerticalSeparator(*args, **kwargs) else: return self.addHorizontalSeparator(*args, **kwargs)
def setAnimationSpeed(
self, name, speed)
def setAnimationSpeed(self, name, speed): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image if speed < 1: speed = 1 self.warn("Setting %s speed to 1. Minimum animation speed is 1.", name) img.anim_speed = int(speed)
def setAutoEntryNumRows(
self, title, rows)
def setAutoEntryNumRows(self, title, rows): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) try: entry.setNumRows(rows) except AttributeError: gui.error("You can only change the number of rows in an AutoEntry, %s is not an AutoEntry.", title)
def setBg(
self, colour, override=False, tint=False)
def setBg(self, colour, override=False, tint=False): if not self.ttkFlag: if self._getContainerProperty('type') == WIDGET_NAMES.RootPage: moved this - it makes the screen do funny stuff self.appWindow.config(background=colour) self.bgLabel.config(background=colour) self._getContainerProperty('container').config(background=colour) for child in self._getContainerProperty('container').winfo_children(): if not self._isWidgetContainer(child): # horrible hack to deal with weird ScrolledText # winfo_children returns ScrolledText as a Frame #Â therefore can't call some functions # this gets the ScrolledText version if gui.GET_WIDGET_CLASS(child) == "Frame": for val in self.widgetManager.group(WIDGET_NAMES.TextArea).values(): if str(val) == str(child): child = val break gui.SET_WIDGET_BG(child, colour, override, tint) else: gui.trace("In ttk mode - trying to set BG to %s", colour) self.ttkStyle.configure(".", background=colour)
def setBgImage(
self, image)
def setBgImage(self, image): image = self._getImage(image, False, False) # make sure it's not using the cache # self.containerStack[0]['container'].config(image=image) # window as a # label doesn't work... self.bgLabel.config(image=image) self.containerStack[0]['container'].image = image # keep a reference!
def setButton(
self, name, text)
def setButton(self, name, text): but = self.widgetManager.get(WIDGET_NAMES.Button, name) try: # try to bind a function command = self.MAKE_FUNC(text, name) but.config(command=command) except: # otherwise change the text but.config(text=text)
def setButtonFont(
self, *args, **kwargs)
def setButtonFont(self, *args, **kwargs): self._fontHelper('buttonFont', *args, **kwargs)
def setButtonImage(
self, name, imgFile, align=None)
def setButtonImage(self, name, imgFile, align=None): but = self.widgetManager.get(WIDGET_NAMES.Button, name) image = self._getImage(imgFile) # works on Mac & Windows :) if align == None: but.config(image=image, text="") if not self.ttk: but.config(justify=LEFT, compound=TOP) else: but.config(compound=CENTER) else: but.config(image=image, compound=align) # but.config(image=image, compound=None, text="") # works on Windows, not Mac but.image = image
def setCanvasEvent(
self, title, item, event, function, add=None)
def setCanvasEvent(self, title, item, event, function, add=None): canvas = self.widgetManager.get(WIDGET_NAMES.Canvas, title) canvas.tag_bind(item, event, function, add)
def setCanvasMap(
self, name, func, coords)
def setCanvasMap(self, name, func, coords): self._setWidgetMap(name, WIDGET_NAMES.Canvas, func, coords)
def setCheckBox(
self, title, ticked=True, callFunction=True)
def setCheckBox(self, title, ticked=True, callFunction=True): cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title) bVar = self.widgetManager.get(WIDGET_NAMES.CheckBox, title, group=WidgetManager.VARS) bVar.set(ticked) if ticked: if not self.ttkFlag: cb.select() else: cb.state(['selected']) else: if not self.ttkFlag: cb.deselect() else: cb.state(['!selected']) # now call function if callFunction: if hasattr(cb, 'cmd'): cb.cmd()
def setCheckBoxBoxBg(
self, title, newCol)
def setCheckBoxBoxBg(self, title, newCol): self.setCheckBoxSelectColour(title, newCol)
def setCheckBoxSelectColour(
self, title, newCol)
def setCheckBoxSelectColour(self, title, newCol): cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title) cb.config(selectcolor=newCol)
def setCheckBoxText(
self, title, text)
def setCheckBoxText(self, title, text): cb = self.widgetManager.get(WIDGET_NAMES.CheckBox, title) cb.DEFAULT_TEXT = text cb.config(text=text)
def setColspan(
self, colspan)
def setColspan(self, colspan): self.containerStack[-1]['colspan'] = colspan
def setDatePicker(
self, title, date='today')
def setDatePicker(self, title, date="today"): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) if date == "today": date = datetime.date.today() self.setOptionBox(title + "_DP_YearOptionBox", str(date.year)) self.setOptionBox(title + "_DP_MonthOptionBox", date.month - 1) self.setOptionBox(title + "_DP_DayOptionBox", date.day - 1)
def setDatePickerChangeFunction(
self, title, function)
def setDatePickerChangeFunction(self, title, function): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) cmd = self.MAKE_FUNC(function, title) self.setOptionBoxChangeFunction(title + "_DP_DayOptionBox", cmd) self.widgetManager.get(WIDGET_NAMES.OptionBox, title + "_DP_DayOptionBox").function = cmd
def setDatePickerFg(
self, name, fg)
def setDatePickerFg(self, name, fg): self.widgetManager.get(WIDGET_NAMES.DatePicker, name) self.setLabelFg(name + "_DP_DayLabel", fg) self.setLabelFg(name + "_DP_MonthLabel", fg) self.setLabelFg(name + "_DP_YearLabel", fg)
def setDatePickerRange(
self, title, startYear, endYear=None)
def setDatePickerRange(self, title, startYear, endYear=None): self.widgetManager.get(WIDGET_NAMES.DatePicker, title) if endYear is None: endYear = datetime.date.today().year years = range(startYear, endYear + 1) self.changeOptionBox(title + "_DP_YearOptionBox", years)
def setEntry(
self, name, text, callFunction=True)
def setEntry(self, name, text, callFunction=True): ent = self.widgetManager.get(WIDGET_NAMES.Entry, name) var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) self._updateEntryDefault(name, mode="set") # now call function with PauseCallFunction(callFunction, var, False): if not ent.isNumeric or self._validateNumericEntry("1", None, text, None, "1", None, None, None): var.set(text)
def setEntryDefault(
self, name, text='default')
def setEntryDefault(self, name, text="default"): entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) # remember current settings - to return to if not hasattr(entry, "oldJustify"): entry.oldJustify = entry.cget('justify') if not hasattr(entry, "oldFg"): if not self.ttkFlag: entry.oldFg = entry.cget('foreground') else: entry.oldFg = entry.cget("style") # configure default stuff entry.default = text entry.DEFAULT_TEXT = text # only show new text if empty self._updateEntryDefault(name, "out") # bind commands to show/remove the default if hasattr(entry, "defaultInEvent"): entry.unbind(entry.defaultInEvent) entry.unbind(entry.defaultOutEvent) in_command = self.MAKE_FUNC(self._entryIn, name) out_command = self.MAKE_FUNC(self._entryOut, name) entry.defaultInEvent = entry.bind("<FocusIn>", in_command, add="+") entry.defaultOutEvent = entry.bind("<FocusOut>", out_command, add="+")
def setEntryInvalid(
self, title)
def setEntryInvalid(self, title): self.setValidationEntry(title, "invalid")
def setEntryLowerCase(
self, name)
def setEntryLowerCase(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) if var.lc_id is not None: var.trace_vdelete('w', var.lc_id) var.lc_id = var.trace('w', self.MAKE_FUNC(self._lowerEntry, name))
def setEntryMaxLength(
self, name, length)
def setEntryMaxLength(self, name, length): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) var.maxLength = length if var.ml_id is not None: var.trace_vdelete('w', var.ml_id) var.ml_id = var.trace('w', self.MAKE_FUNC(self._limitEntry, name))
def setEntryUpperCase(
self, name)
def setEntryUpperCase(self, name): var = self.widgetManager.get(WIDGET_NAMES.Entry, name, group=WidgetManager.VARS) if var.uc_id is not None: var.trace_vdelete('w', var.uc_id) var.uc_id = var.trace('w', self.MAKE_FUNC(self._upperEntry, name))
def setEntryValid(
self, title)
def setEntryValid(self, title): self.setValidationEntry(title, "valid")
def setEntryWaitingValidation(
self, title)
def setEntryWaitingValidation(self, title): self.setValidationEntry(title, "wait")
def setExpand(
self, exp)
def setExpand(self, exp): if exp is None or exp.lower() == "none": self.containerStack[-1]['expand'] = "NONE" elif exp.lower() == "row": self.containerStack[-1]['expand'] = "ROW" elif exp.lower() == "column": self.containerStack[-1]['expand'] = "COLUMN" else: self.containerStack[-1]['expand'] = "ALL"
def setFastStop(
self, fast=True)
def setFastStop(self, fast=True): self._fastStop = fast
def setFg(
self, colour, override=False)
def setFg(self, colour, override=False): if not self.ttkFlag: self.containerStack[-1]['fg']=colour gui.SET_WIDGET_FG(self._getContainerProperty('container'), colour, override) for child in self._getContainerProperty('container').winfo_children(): if not self._isWidgetContainer(child): gui.SET_WIDGET_FG(child, colour, override) else: gui.trace("In ttk mode - trying to set FG to %s", colour) self.ttkStyle.configure("TLabel", foreground=colour) self.ttkStyle.configure("TFrame", foreground=colour)
def setFocus(
self, name)
def setFocus(self, name): entry = self.widgetManager.get(WIDGET_NAMES.Entry, name) entry.focus_set()
def setFont(
self, *args, **kwargs)
def setFont(self, *args, **kwargs): self.setInputFont(*args, **kwargs) self.setLabelFont(*args, **kwargs) self.setButtonFont(*args, **kwargs)
def setFullscreen(
self, title=None)
sets the specified window to be fullscreen if no title, will set the main GUI
def setFullscreen(self, title=None): """ sets the specified window to be fullscreen if no title, will set the main GUI """ try: container = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) except: container = self._getTopLevel() if not container.isFullscreen: container.isFullscreen = True container.attributes('-fullscreen', True) container.escapeBindId = container.bind('<Escape>', self.MAKE_FUNC(self.exitFullscreen, container), "+")
def setGoogleMapLocation(
self, title, location)
def setGoogleMapLocation(self, title, location): self.searchGoogleMap(title, location)
def setGoogleMapMarker(
self, title, location, size=None, colour=None, label=None, replace=False)
def setGoogleMapMarker(self, title, location, size=None, colour=None, label=None, replace=False): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if len(location) == 0: gMap.removeMarkers() else: gMap.addMarker(location, size, colour, label, replace)
def setGoogleMapProxy(
self, title, proxyString)
def setGoogleMapProxy(self, title, proxyString): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) gMap.setProxyString(proxyString)
def setGoogleMapSize(
self, title, size)
def setGoogleMapSize(self, title, size): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) gMap.setSize(size)
def setGoogleMapTerrain(
self, title, terrain)
def setGoogleMapTerrain(self, title, terrain): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if terrain not in gMap.TERRAINS: raise Exception("Invalid terrain. Must be one of " + str(gMap.TERRAINS)) gMap.changeTerrain(terrain)
def setGoogleMapZoom(
self, title, mod)
def setGoogleMapZoom(self, title, mod): self. zoomGoogleMap(title, mod)
def setGridHeaders(
self, title, data)
def setGridHeaders(self, title, data): return self.setTableHeaders(title, data)
def setGuiPadding(
self, x, y=None)
sets the padding around the border of the GUI
def setGuiPadding(self, x, y=None): """ sets the padding around the border of the GUI """ x, y = gui.PARSE_TWO_PARAMS(x, y) self.containerStack[0]['container'].config(padx=x, pady=y)
def setIPadX(
self, x=0)
def setIPadX(self, x=0): self.setInPadX(x)
def setIPadY(
self, y=0)
def setIPadY(self, y=0): self.setInPadY(y)
def setIPadding(
self, x, y=None)
def setIPadding(self, x, y=None): self.setInPadding(x, y)
def setIcon(
self, image)
def setIcon(self, image): container = self._getTopLevel() container.winIcon = image if image.endswith('.ico'): container.wm_iconbitmap(image) else: icon = self._getImage(image) container.iconphoto(True, icon)
def setImage(
self, name, imageFile, internal=False)
def setImage(self, name, imageFile, internal=False): label = self.widgetManager.get(WIDGET_NAMES.Image, name) imageFile = self.getImagePath(imageFile) # only set the image if it's different if label.image is not None and label.image.path == imageFile: self.warn("Not updating %s, %s hasn't changed." , name, imageFile) return elif imageFile is None: return else: image = self._getImage(imageFile) self._populateImage(name, image, internal)
def setImageData(
self, name, imageData, fmt='gif')
def setImageData(self, name, imageData, fmt="gif"): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = self._getImageData(imageData, fmt=fmt) self._populateImage(name, image)
def setImageLocation(
self, location)
def setImageLocation(self, location): if os.path.isdir(location): self.userImages = location else: raise Exception("Invalid image location: " + location)
def setImageMap(
self, name, func, coords)
def setImageMap(self, name, func, coords): self._setWidgetMap(name, WIDGET_NAMES.Image, func, coords)
def setImageMouseOver(
self, title, overImg)
def setImageMouseOver(self, title, overImg): lab = self.widgetManager.get(WIDGET_NAMES.Image, title) # first check over image & cache it fullPath = self.getImagePath(overImg) self.topLevel.after(0, self._getImage, fullPath) leaveImg = lab.image.path lab.bind("<Leave>", lambda e: self.setImage(title, leaveImg, True)) lab.bind("<Enter>", lambda e: self.setImage(title, fullPath, True)) lab.hasMouseOver = True
def setImageSize(
self, name, width, height)
def setImageSize(self, name, width, height): img = self.widgetManager.get(WIDGET_NAMES.Image, name) img.config(height=height, width=width)
def setInPadX(
self, x=0)
def setInPadX(self, x=0): self.containerStack[-1]['ipadx'] = x
def setInPadY(
self, y=0)
def setInPadY(self, y=0): self.containerStack[-1]['ipady'] = y
def setInPadding(
self, x, y=None)
def setInPadding(self, x, y=None): x, y = gui.PARSE_TWO_PARAMS(x, y) self.containerStack[-1]['ipadx'] = x self.containerStack[-1]['ipady'] = y
def setInputFont(
self, *args, **kwargs)
def setInputFont(self, *args, **kwargs): self._fontHelper('inputFont', *args, **kwargs)
def setLabel(
self, name, text)
def setLabel(self, name, text): lab = self.widgetManager.get(WIDGET_NAMES.Label, name) lab.config(text=text)
def setLabelFont(
self, *args, **kwargs)
def setLabelFont(self, *args, **kwargs): kwargs = self._fontHelper('labelFont', *args, **kwargs) if kwargs is not None: self.tableFont.config(**kwargs) # need better way to register font change events on tables for k, v in self.widgetManager.group(WIDGET_NAMES.Table).items(): v.config(font=self.tableFont) linkArgs = kwargs.copy() linkArgs['underline'] = True linkArgs['weight'] = 'bold' self._linkFont.config(**linkArgs)
def setLabelFrameTitle(
self, title, newTitle)
def setLabelFrameTitle(self, title, newTitle): frame = self.widgetManager.get(WIDGET_NAMES.LabelFrame, title) frame.config(text=newTitle)
def setLanguage(
self, language)
wrapper for changeLanguage()
def setLanguage(self, language): """ wrapper for changeLanguage() """ self.changeLanguage(language)
def setLink(
self, title, func)
def setLink(self, title, func): link = self.widgetManager.get(WIDGET_NAMES.Link, title) if not callable(func) and not hasattr(func, '__call__'): link.registerWebpage(func) else: myF = self.MAKE_FUNC(func, title) link.registerCallback(myF)
def setListBoxGroup(
self, name, group=True)
def setListBoxGroup(self, name, group=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name) group = not group lb.config(exportselection=group)
def setListBoxMulti(
self, title, multi=True)
def setListBoxMulti(self, title, multi=True): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) if multi: lb.config(selectmode=EXTENDED) else: lb.config(selectmode=BROWSE)
def setListBoxRows(
self, name, rows)
def setListBoxRows(self, name, rows): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, name) lb.config(height=rows)
def setListItem(
self, title, item, newVal, first=False)
def setListItem(self, title, item, newVal, first=False): for pos in self._getListPositions(title, item): self.setListItemAtPos(title, pos, newVal) if first: break
def setListItemAtPos(
self, title, pos, newVal)
def setListItemAtPos(self, title, pos, newVal): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.delete(pos) lb.insert(pos, newVal)
def setListItemAtPosBg(
self, title, pos, col)
def setListItemAtPosBg(self, title, pos, col): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.itemconfig(pos, bg=col)
def setListItemAtPosFg(
self, title, pos, col)
def setListItemAtPosFg(self, title, pos, col): lb = self.widgetManager.get(WIDGET_NAMES.ListBox, title) lb.itemconfig(pos, fg=col)
def setListItemBg(
self, title, item, col)
def setListItemBg(self, title, item, col): for pos in self._getListPositions(title, item): self.setListItemAtPosBg(title, pos, col)
def setListItemFg(
self, title, item, col)
def setListItemFg(self, title, item, col): for pos in self._getListPositions(title, item): self.setListItemAtPosFg(title, pos, col)
def setLocation(
self, x, y=None, ignoreSettings=None, win=None, up=0)
called to set the GUI's position on screen
def setLocation(self, x, y=None, ignoreSettings=None, win=None, up=0): """ called to set the GUI's position on screen """ if win is None: win = self._getTopLevel() gui.SET_LOCATION(x, y, ignoreSettings, win, up)
def setLogFile(
fileName)
sets the filename for logging messages
@staticmethod def setLogFile(fileName): """ sets the filename for logging messages """ # Remove all handlers associated with the root logger object. for handler in logging.root.handlers[:]: logging.root.removeHandler(handler) logging.basicConfig(level=logging.INFO, filename=fileName, format='%(asctime)s %(name)s:%(levelname)s: %(message)s') gui.info("Switched to logFile: %s", fileName)
def setLogLevel(
level)
main function for setting the logging level provide one of: INFO, DEBUG, WARNING, ERROR, CRITICAL, EXCEPTION, None
@staticmethod def setLogLevel(level): """ main function for setting the logging level provide one of: INFO, DEBUG, WARNING, ERROR, CRITICAL, EXCEPTION, None """ logging.getLogger("appJar").setLevel(getattr(logging, level.upper())) gui.info("Log level changed to: %s", level)
def setMenuCheckBox(
self, menu, name, value=None)
def setMenuCheckBox(self, menu, name, value=None): self._setMenu(menu, name, value, "cb")
def setMenuIcon(
self, menu, title, icon, align='left')
def setMenuIcon(self, menu, title, icon, align="left"): image = os.path.join(self.icon_path, icon.lower() + ".png") with PauseLogger(): self.setMenuImage(menu, title, image, align)
def setMenuImage(
self, menu, title, image, align='left')
def setMenuImage(self, menu, title, image, align="left"): theMenu = self.widgetManager.get(WIDGET_NAMES.Menu, menu) imageObj = self._getImage(image) if 16 != imageObj.width() or imageObj.width() != imageObj.height(): self.warn("Invalid image resolution for menu item %s (%s) - should be 16x16", title, image) #imageObj = imageObj.subsample(2,2) try: theMenu.entryconfigure(title, image=imageObj, compound=align) except TclError: gui.error("Unable to set image for menu item: %s, in menu: %s - item not found", title, menu)
def setMenuRadioButton(
self, menu, name, value)
def setMenuRadioButton(self, menu, name, value): self._setMenu(menu, name, value, "rb")
def setMessage(
self, title, text)
def setMessage(self, title, text): mess = self.widgetManager.get(WIDGET_NAMES.Message, title) mess.config(text=text)
def setMessageAspect(
self, title, aspect)
set a new aspect ratio for the text in this widget
def setMessageAspect(self, title, aspect): """ set a new aspect ratio for the text in this widget """ mess = self.widgetManager.get(WIDGET_NAMES.Message, title) mess.config(aspect=aspect)
def setMeter(
self, name, value=0.0, text=None)
def setMeter(self, name, value=0.0, text=None): item = self.widgetManager.get(WIDGET_NAMES.Meter, name) item.set(value, text)
def setMeterFill(
self, name, colour)
def setMeterFill(self, name, colour): item = self.widgetManager.get(WIDGET_NAMES.Meter, name) item.configure(fill=colour)
def setMicroBitImage(
self, title, image)
def setMicroBitImage(self, title, image): self.widgetManager.get(WIDGET_NAMES.MicroBit, title).show(image)
def setMicroBitPixel(
self, title, x, y, brightness)
def setMicroBitPixel(self, title, x, y, brightness): self.widgetManager.get(WIDGET_NAMES.MicroBit, title).set_pixel(x, y, brightness)
def setMinSize(
self, container=None, size=None)
sets a minimum size for the specified container - defaults to the whole GUI
def setMinSize(self, container=None, size=None): """ sets a minimum size for the specified container - defaults to the whole GUI """ if container is None: container = self.topLevel if size is None: size = (gui.GET_DIMS(container)["r_width"], gui.GET_DIMS(container)["r_height"]) container.ms = size container.minsize(size[0], size[1]) gui.trace("Minsize set to: %s", size)
def setOnTop(
self, stay=True)
def setOnTop(self, stay=True): self._getTopLevel().attributes("-topmost", stay) gui.trace("Staying on top set to: %s", stay)
def setOptionBox(
self, title, index, value=True, callFunction=True, override=False)
Main purpose is to select/deselect the item at the specified position But will also: delete an item if value is set to None or rename an item if value is set to a String
:param title: the OptionBox to change :param index: the position or value of the item to select/delete :param value: determines what to do to the item: if set to None, will delete the item, else it sets the items state :param callFunction: whether to generate an event to notify that the widget has changed :param override: if set to True, allows a disabled item to be selected :returns: None :raises ItemLookupError: if the title can't be found
def setOptionBox(self, title, index, value=True, callFunction=True, override=False): """ Main purpose is to select/deselect the item at the specified position But will also: delete an item if value is set to None or rename an item if value is set to a String :param title: the OptionBox to change :param index: the position or value of the item to select/delete :param value: determines what to do to the item: if set to None, will delete the item, else it sets the items state :param callFunction: whether to generate an event to notify that the widget has changed :param override: if set to True, allows a disabled item to be selected :returns: None :raises ItemLookupError: if the title can't be found """ box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) if box.kind == "ticks": gui.trace("Updating tickOptionBox") ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) if index is None: gui.trace("Index empty - nothing to update") return elif index in ticks: gui.trace("Updating: %s", index) tick = ticks[index] try: index_num = box.options.index(index) except: self.warn("Unknown tick: %s in OptionBox: %s", index, title) return with PauseCallFunction(callFunction, tick, useVar=False): if value is None: # then we need to delete it gui.trace("Deleting tick: %s from OptionBox %s", index, title) box['menu'].delete(index_num) del(box.options[index_num]) self.widgetManager.remove(WIDGET_NAMES.TickOptionBox, title, index, group=WidgetManager.VARS) elif isinstance(value, bool): gui.trace("Updating tick: %s from OptionBox: %s to: %s", index, title, value) tick.set(value) else: gui.trace("Renaming tick: %s from OptionBox: %s to: %s", index, title, value) ticks = self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS) ticks[value] = ticks.pop(index) box.options[index_num] = value self.changeOptionBox(title, box.options) for tick in ticks: self.widgetManager.get(WIDGET_NAMES.TickOptionBox, title, group=WidgetManager.VARS)[tick].set(ticks[tick].get()) else: if value is None: self.warn("Unknown tick in deleteOptionBox: %s in OptionBox: %s" , index, title) else: self.warn("Unknown tick in setOptionBox: %s in OptionBox: %s", index, title) else: gui.trace("Updating regular optionBox: %s at: %s to: %s", title, index, value) count = len(box.options) if count > 0: if index is None: index = 0 if not isinstance(index, int): try: index = box.options.index(index) except: if value is None: self.warn("Unknown option in deleteOptionBox: %s in OptionBox: %s", index, title) else: self.warn("Unknown option in setOptionBox: %s in OptionBox: %s", index, title) return gui.trace("--> index now: %s", index) if index < 0 or index > count - 1: self.warn("Invalid option: %s. Should be between 0 and %s." , count-1, index) else: if value is None: # then we can delete it... gui.trace("Deleting option: %s from OptionBox: %s", index, title) box['menu'].delete(index) del(box.options[index]) self.setOptionBox(title, 0, callFunction=False, override=override) elif isinstance(value, bool): gui.trace("Updating: OptionBox: %s to: %s", title, index) with PauseCallFunction(callFunction, box): if not box['menu'].invoke(index): if override: gui.trace("Setting OptionBox: %s to disabled option: %s", title, index) box["menu"].entryconfigure(index, state="normal") box['menu'].invoke(index) box["menu"].entryconfigure(index, state="disabled") else: self.warn("Unable to set disabled option: %s in OptionBox %s. Try setting 'override=True'", index, title) else: gui.trace("Invoked item: %s", index) else: gui.trace("Renaming: %s from OptionBox: %s to: %s", index, title, value) pos = box.options.index(self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).get()) box.options[index] = value self.changeOptionBox(title, box.options, pos) else: self.widgetManager.get(WIDGET_NAMES.OptionBox, title, group=WidgetManager.VARS).set("") self.warn("No items to select from: %s", title)
def setOptionBoxDisabledChar(
self, title, disabled='-')
def setOptionBoxDisabledChar(self, title, disabled="-"): box = self.widgetManager.get(WIDGET_NAMES.OptionBox, title) box.disabled = disabled self._disableOptionBoxSeparators(box)
def setPadX(
self, x=0)
set the current container's external grid padding
def setPadX(self, x=0): """ set the current container's external grid padding """ self.containerStack[-1]['padx'] = x
def setPadY(
self, y=0)
set the current container's external grid padding
def setPadY(self, y=0): """ set the current container's external grid padding """ self.containerStack[-1]['pady'] = y
def setPadding(
self, x, y=None)
sets the padding around the border of the current container
def setPadding(self, x, y=None): """ sets the padding around the border of the current container """ x, y = gui.PARSE_TWO_PARAMS(x, y) self.containerStack[-1]['padx'] = x self.containerStack[-1]['pady'] = y
def setPagedWindowButtons(
self, title, buttons)
def setPagedWindowButtons(self, title, buttons): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) if not isinstance(buttons, list) or len(buttons) != 2: raise Exception( "You must provide a list of two strings for setPagedWinowButtons()") pager.setPrevButton(buttons[0]) pager.setNextButton(buttons[1])
def setPagedWindowButtonsTop(
self, title, top=True)
def setPagedWindowButtonsTop(self, title, top=True): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.setNavPositionTop(top)
def setPagedWindowFunction(
self, title, func)
def setPagedWindowFunction(self, title, func): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) command = self.MAKE_FUNC(func, title) pager.registerPageChangeEvent(command)
def setPagedWindowPage(
self, title, page)
def setPagedWindowPage(self, title, page): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.showPage(page)
def setPagedWindowTitle(
self, title, pageTitle)
def setPagedWindowTitle(self, title, pageTitle): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.setTitle(pageTitle)
def setPaneSashPosition(
self, pos, pane=None)
def setPaneSashPosition(self, pos, pane=None): # convert to a percentage if needed if pos > 1: pos = pos / 100.0 if pane is None: if self._getContainerProperty('type') == WIDGET_NAMES.PanedFrame: pane = self._getContainerProperty('container') elif self.containerStack[-2]['type'] == WIDGET_NAMES.PanedFrame: pane = self.containerStack[-2]['container'] elif self._getContainerProperty('type') == WIDGET_NAMES.Pane: pane = self._getContainerProperty('container').parent else: gui.error("Unable to set sash position - can't find a pane") return elif type(pane) == str: pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, pane) if pane.cget('orient') == 'horizontal': w = int(pane.winfo_width() * pos) try: pane.sash_place(0, w, 0) gui.trace('Set horizontal pane: %s to position: %s', pane, pos) except TclError as e: # no sash to configure - last pane pass else: h = int(pane.winfo_height() * pos) try: pane.sash_place(0, 0, h) gui.trace('Set vertical pane: %s to position: %s', pane, pos) except TclError as e: # no sash to configure - last pane pass
def setPanedFrameVertical(
self, window)
def setPanedFrameVertical(self, window): pane = self.widgetManager.get(WIDGET_NAMES.PanedFrame, window) pane.config(orient=VERTICAL)
def setPieChart(
self, title, name, value)
def setPieChart(self, title, name, value): pie = self.widgetManager.get(WIDGET_NAMES.PieChart, title) pie.setValue(name, value)
def setPollTime(
self, time)
Set a frequency for executing queued functions events will fire in order of being added, after sleeping for time
def setPollTime(self, time): """ Set a frequency for executing queued functions events will fire in order of being added, after sleeping for time """ self.pollTime = time
def setProperties(
self, title, props, callFunction=True)
def setProperties(self, title, props, callFunction=True): p = self.widgetManager.get(WIDGET_NAMES.Properties, title) p.addProperties(props, callFunction=callFunction)
def setPropertiesBoxBg(
self, title, newCol)
def setPropertiesBoxBg(self, title, newCol): self.setPropertiesSelectColour(title, newCol)
def setPropertiesSelectColour(
self, title, newCol)
def setPropertiesSelectColour(self, title, newCol): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.config(selectcolor=newCol)
def setProperty(
self, title, prop, value=False, callFunction=True)
def setProperty(self, title, prop, value=False, callFunction=True): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.addProperty(prop, value, callFunction=callFunction)
def setPropertyText(
self, title, prop, newText=None)
def setPropertyText(self, title, prop, newText=None): props = self.widgetManager.get(WIDGET_NAMES.Properties, title) props.renameProperty(prop, newText)
def setRadioButton(
self, title, value, callFunction=True)
def setRadioButton(self, title, value, callFunction=True): ident = title + "-" + value self.widgetManager.get(WIDGET_NAMES.RadioButton, ident) # now call function var = self.widgetManager.get(WIDGET_NAMES.RadioButton, title, group=WidgetManager.VARS) with PauseCallFunction(callFunction, var, False): var.set(value)
def setRadioSquare(
self, title, square=True)
def setRadioSquare(self, title, square=True): if self.platform == self.MAC: gui.warn("Square radiobuttons not available on Mac, for radiobutton %s", title) elif not self.ttkFlag: for k, v in self.widgetManager.group(WIDGET_NAMES.RadioButton).items(): if k.startswith(title+"-"): if square: v.config(indicatoron=1) else: v.config(indicatoron=0) else: gui.warn("Square radiobuttons not available in ttk mode, for radiobutton %s", title)
def setRadioTick(
self, title, tick=True)
def setRadioTick(self, title, tick=True): self.warn("Deprecated function (%s) used for %s -> %s use %s instead", 'setRadioTick', 'radioButton', title, 'setRadioSquare') self.setRadioSquare(title, square=tick)
def setResizable(
self, canResize=True)
def setResizable(self, canResize=True): self._getTopLevel().isResizable = canResize if self._getTopLevel().isResizable: self._getTopLevel().resizable(True, True) else: self._getTopLevel().resizable(False, False)
def setRow(
self, row)
def setRow(self, row): self.containerStack[-1]['emptyRow'] = row
def setRowspan(
self, rowspan)
def setRowspan(self, rowspan): self.containerStack[-1]['rowspan'] = rowspan
def setScale(
self, title, pos, callFunction=True)
def setScale(self, title, pos, callFunction=True): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) with PauseCallFunction(callFunction, sc): sc.set(pos)
def setScaleHorizontal(
self, title)
def setScaleHorizontal(self, title): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(orient=HORIZONTAL)
def setScaleIncrement(
self, title, increment)
def setScaleIncrement(self, title, increment): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.increment = increment
def setScaleLength(
self, title, length)
def setScaleLength(self, title, length): if not self.ttkFlag: sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(sliderlength=length) else: self.warn("ttk: setScaleLength() not supported: %s", title)
def setScaleRange(
self, title, start, end, curr=None)
def setScaleRange(self, title, start, end, curr=None): if curr is None: curr = start sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(from_=start, to=end) self.setScale(title, curr) # set the increment as 10% try: res = sc.cget("resolution") diff = int((((end - start)/res)/10)+0.99) # add 0.99 to round up... sc.increment = diff except: pass # resolution not supported in ttk
def setScaleVertical(
self, title)
def setScaleVertical(self, title): sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(orient=VERTICAL)
def setSetting(
self, name, value)
adds a setting to the settings file
def setSetting(self, name, value): """ adds a setting to the settings file """ self.externalSettings[name] = value
def setSize(
self, geom, height=None, ignoreSettings=None)
called to update screen geometry can take a geom string, or a width & height can override ignoreSettings if desired
def setSize(self, geom, height=None, ignoreSettings=None): """ called to update screen geometry can take a geom string, or a width & height can override ignoreSettings if desired """ container = self._getTopLevel() if ignoreSettings is not None: container.ignoreSettings = ignoreSettings try: geom = geom.lower() except: # ignore - other data types allowed pass if geom == "fullscreen": self.setFullscreen() elif geom is not None: if height is not None: geom=(geom, height) elif not isinstance(geom, list) and not isinstance(geom, tuple): geom, loc = gui.SPLIT_GEOM(geom) size = "%sx%s" % (int(geom[0]), int(geom[1])) gui.trace("Setting size: %s", size) # warn the user that their geom is not big enough dims = gui.GET_DIMS(container) if geom[0] < dims["b_width"] or geom[1] < dims["b_height"]: self.trace("Specified dimensions (%s, %s) less than requested dimensions (%s, %s)", geom[0], geom[1], dims["b_width"], dims["b_height"]) # and set it as the minimum size if not hasattr(container, 'ms'): self.setMinSize(container, geom) self.exitFullscreen() container.geometry(size)
def setSoundLocation(
self, location)
def setSoundLocation(self, location): if os.path.isdir(location): self.userSounds = location else: raise Exception("Invalid sound location: " + location)
def setSpinBox(
self, title, value, callFunction=True)
def setSpinBox(self, title, value, callFunction=True): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) vals = spin.cget("values") # .split() vals = self._getSpinBoxValsAsList(vals) val = str(value) if val not in vals: raise Exception( "Invalid value: " + val + ". Not in SpinBox: " + title + "=" + str(vals)) self._setSpinBoxVal(spin, val, callFunction)
def setSpinBoxPos(
self, title, pos, callFunction=True)
def setSpinBoxPos(self, title, pos, callFunction=True): spin = self.widgetManager.get(WIDGET_NAMES.SpinBox, title) vals = spin.cget("values") # .split() vals = self._getSpinBoxValsAsList(vals) pos = int(pos) if pos < 0 or pos >= len(vals): raise Exception( "Invalid position: " + str(pos) + ". No position in SpinBox: " + title + "=" + str(vals)) pos = len(vals) - 1 - pos val = vals[pos] self._setSpinBoxVal(spin, val, callFunction)
def setStartFrame(
self, title, num)
def setStartFrame(self, title, num): self.widgetManager.get(WIDGET_NAMES.FrameStack, title).setStartFrame(num)
def setStartFunction(
self, func)
def setStartFunction(self, func): f = self.MAKE_FUNC(func, "start") self.topLevel.startFunction = f
def setStatusFont(
self, *args, **kwargs)
def setStatusFont(self, *args, **kwargs): self._fontHelper('statusFont', *args, **kwargs)
def setStatusbar(
self, text, field=0)
def setStatusbar(self, text, field=0): if self.hasStatus: if field is None: for status in self._statusFields: status.config(text=self._getFormatStatus(text)) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(text=self._getFormatStatus(text)) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1))
def setStatusbarBg(
self, colour, field=None)
def setStatusbarBg(self, colour, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(background=colour) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(background=colour) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1))
def setStatusbarFg(
self, colour, field=None)
def setStatusbarFg(self, colour, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(foreground=colour) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(foreground=colour) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1))
def setStatusbarHeader(
self, header)
def setStatusbarHeader(self, header): if self.hasStatus: self.header = header
def setStatusbarWidth(
self, width, field=None)
def setStatusbarWidth(self, width, field=None): if self.hasStatus: if field is None: for status in self._statusFields: status.config(width=width) elif field >= 0 and field < len(self._statusFields): self._statusFields[field].config(width=width) else: raise Exception("Invalid status field: " + str(field) + ". Must be between 0 and " + str(len(self._statusFields) - 1))
def setSticky(
self, sticky)
def setSticky(self, sticky): self.containerStack[-1]['sticky'] = sticky
def setStopFunction(
self, function)
Set a function to call when the GUI is quit. Must return True or False
def setStopFunction(self, function): """ Set a function to call when the GUI is quit. Must return True or False """ tl = self._getTopLevel() tl.stopFunction = function # link to exit item in topMenu # only if in root if self._getContainerProperty('type') != WIDGET_NAMES.SubWindow: tl.createcommand('exit', self.stop)
def setStretch(
self, exp)
def setStretch(self, exp): self.setExpand(exp)
def setSubWindowLocation(
self, title, x, y)
def setSubWindowLocation(self, title, x, y): self.widgetManager.get(WIDGET_NAMES.SubWindow, title).setLocation(x, y)
def setTabBg(
self, title, tab, colour)
def setTabBg(self, title, tab, colour): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) tab = nb.getTab(tab) gui.SET_WIDGET_BG(tab, colour) # tab.config(bg=colour) #gui.SET_WIDGET_BG(tab, colour) for child in tab.winfo_children(): gui.SET_WIDGET_BG(child, colour)
def setTabFont(
self, title, **kwargs)
def setTabFont(self, title, **kwargs): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.setFont(**kwargs)
def setTabText(
self, title, tab, newText=None)
def setTabText(self, title, tab, newText=None): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.renameTab(tab, newText)
def setTabbedFrameChangeCommand(
self, title, func)
def setTabbedFrameChangeCommand(self, title, func): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) command = self.MAKE_FUNC(func, title) nb.config(command=command)
def setTabbedFrameDisableAllTabs(
self, title, disabled=True)
def setTabbedFrameDisableAllTabs(self, title, disabled=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.disableAllTabs(disabled)
def setTabbedFrameDisabledTab(
self, title, tab, disabled=True)
def setTabbedFrameDisabledTab(self, title, tab, disabled=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.disableTab(tab, disabled)
def setTabbedFrameSelectedTab(
self, title, tab, callFunction=True)
def setTabbedFrameSelectedTab(self, title, tab, callFunction=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) try: nb.changeTab(tab, callFunction) except KeyError: raise ItemLookupError("Invalid tab name: " + str(tab))
def setTabbedFrameTabExpand(
self, title, expand=True)
def setTabbedFrameTabExpand(self, title, expand=True): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.expandTabs(expand)
def setTableEditFunction(
self, title, func)
def setTableEditFunction(self, title, func): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) cmd = self.MAKE_FUNC(func, title) grid.config(edit=cmd)
def setTableHeaders(
self, title, data)
change the headers in the specified table
def setTableHeaders(self, title, data): ''' change the headers in the specified table ''' grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.setHeaders(data)
def setTextArea(
self, title, text, end=True, callFunction=True, tag=None)
Add the supplied text to the specified TextArea
:param title: the TextArea to change :param text: the text to add to the TextArea :param end: where to insert the text, by default it is added to the end. Set end to False to add to the beginning. :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found
def setTextArea(self, title, text, end=True, callFunction=True, tag=None): """ Add the supplied text to the specified TextArea :param title: the TextArea to change :param text: the text to add to the TextArea :param end: where to insert the text, by default it is added to the end. Set end to False to add to the beginning. :param callFunction: whether to generate an event to notify that the widget has changed :returns: None :raises ItemLookupError: if the title can't be found """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.pauseCallFunction(callFunction) # in case it's disabled _state = ta.cget('state') ta.config(state='normal') if end: pos = ta.index('end -1c linestart') ta.insert(END, text) ta.see(END) if tag is not None: self.textAreaTagRange(title, tag, pos) else: ta.insert('1.0', text) ta.see('1.0') if tag is not None: ta.textAreaTagPattern(title, tag, text) ta.config(state=_state) ta.resumeCallFunction()
def setTextAreaFont(
self, title, **kwargs)
changes the font of a text area
def setTextAreaFont(self, title, **kwargs): """ changes the font of a text area """ self.widgetManager.get(WIDGET_NAMES.TextArea, title).setFont(**kwargs)
def setTitle(
self, title)
def setTitle(self, title): self._getTopLevel().title(title)
def setToggleFrameText(
self, title, newText)
def setToggleFrameText(self, title, newText): toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title) toggle.config(text=newText)
def setToolbarBg(
self, bg)
def setToolbarBg(self, bg): self.tb.BG_COLOR = bg if not self.ttkFlag: self.tb.config(bg=self.tb.BG_COLOR) if gui.GET_PLATFORM() == gui.MAC: for name, val in self.widgetManager.group(WIDGET_NAMES.Toolbar).items(): val.config(highlightbackground=self.tb.BG_COLOR) # config the pin button if exists if self.tb.pinBut is not None: self.tb.pinBut.config(bg=self.tb.BG_COLOR) else: self.ttkStyle.configure("Toolbar.TFrame", background=self.tb.BG_COLOR) self.ttkStyle.configure("Toolbar.TLabel", background=self.tb.BG_COLOR)
def setToolbarButtonDisabled(
self, name, disabled=True)
def setToolbarButtonDisabled(self, name, disabled=True): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) if disabled: self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=DISABLED) else: self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(state=NORMAL)
def setToolbarButtonEnabled(
self, name)
def setToolbarButtonEnabled(self, name): self.setToolbarButtonDisabled(name, False)
def setToolbarDisabled(
self, disabled=True)
def setToolbarDisabled(self, disabled=True): for but in self.widgetManager.group(WIDGET_NAMES.Toolbar).keys(): if disabled: self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=DISABLED) else: self.widgetManager.get(WIDGET_NAMES.Toolbar, but).config(state=NORMAL) if self.tb.pinBut is not None: if disabled: # this fails if not bound if self.tb.pinBut.eventId: self.tb.pinBut.unbind("<Button-1>", self.tb.pinBut.eventId) self.tb.pinBut.eventId = None self._disableTooltip(self.tb.pinBut) self.tb.pinBut.config(cursor="") else: if gui.GET_PLATFORM() == gui.MAC: self.tb.pinBut.config(cursor="pointinghand") elif gui.GET_PLATFORM() in [gui.WINDOWS, gui.LINUX]: self.tb.pinBut.config(cursor="hand2") self.tb.pinBut.eventId = self.tb.pinBut.bind("<Button-1>", self._toggletb) self._enableTooltip(self.tb.pinBut)
def setToolbarEnabled(
self)
def setToolbarEnabled(self): self.setToolbarDisabled(False)
def setToolbarIcon(
self, name, icon)
def setToolbarIcon(self, name, icon): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) imgFile = os.path.join(self.icon_path, icon.lower() + ".png") with PauseLogger(): self.setToolbarImage(name, imgFile)
def setToolbarImage(
self, name, imgFile)
def setToolbarImage(self, name, imgFile): if (name not in self.widgetManager.group(WIDGET_NAMES.Toolbar)): raise Exception("Unknown toolbar name: " + name) image = self._getImage(imgFile) self.widgetManager.get(WIDGET_NAMES.Toolbar, name).config(image=image) self.widgetManager.get(WIDGET_NAMES.Toolbar, name).image = image
def setToolbarPinned(
self, pinned=True)
def setToolbarPinned(self, pinned=True): self.tb.pinned = pinned self._setPinBut() if not self.tb.pinned: if self.tb.pinBut is not None: try: self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "unpin.gif")) except: pass self.tb.makeMinBar() self.tb._minToolbar() else: if self.tb.pinBut is not None: try: self.tb.pinBut.image = self._getImage(os.path.join(self.icon_path, "pin.gif")) except: pass self.tb._maxToolbar() if self.tb.pinBut is not None: self.tb.pinBut.config(image=self.tb.pinBut.image)
def setTransparency(
self, percentage)
def setTransparency(self, percentage): if self.platform == self.LINUX: self.warn("Transparency not supported on LINUX") else: if percentage > 1: percentage = float(percentage) / 100 self._getTopLevel().attributes("-alpha", percentage)
def setTreeBg(
self, title, colour)
def setTreeBg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setBgColour(colour)
def setTreeClickFunction(
self, title, func)
def setTreeClickFunction(self, title, func): if func is not None: tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.item.registerClick(title, func)
def setTreeColours(
self, title, fg=None, bg=None, fgH=None, bgH=None)
def setTreeColours(self, title, fg=None, bg=None, fgH=None, bgH=None): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setAllColours(bg, fg, bgH, fgH)
def setTreeDoubleClickFunction(
self, title, func)
def setTreeDoubleClickFunction(self, title, func): if func is not None: tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.item.registerDblClick(title, func)
def setTreeEditFunction(
self, title, func)
def setTreeEditFunction(self, title, func): if func is not None: tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) command = self.MAKE_FUNC(func, title) tree.registerEditEvent(command)
def setTreeEditable(
self, title, value=True)
def setTreeEditable(self, title, value=True): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.item.setCanEdit(value)
def setTreeFg(
self, title, colour)
def setTreeFg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setFgColour(colour)
def setTreeHighlightBg(
self, title, colour)
def setTreeHighlightBg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setBgHColour(colour)
def setTreeHighlightFg(
self, title, colour)
def setTreeHighlightFg(self, title, colour): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.setFgHColour(colour)
def setTtkTheme(
self, theme=None)
sets the ttk theme to use
def setTtkTheme(self, theme=None): """ sets the ttk theme to use """ self.ttkStyle = ttk.Style() gui.trace("Switching ttk theme to: %s", theme) if theme is not None: try: self.ttkStyle.theme_use(theme) except: gui.trace("no basic ttk theme named %s found, searching for additional themes", theme) self._loadTtkThemes() if not ThemedStyle: self.error("ttk theme: %s unavailable. Try one of: %s", theme, str(self.ttkStyle.theme_names())) else: self.ttkStyle.set_theme(theme) # set up our ttk styles self.ttkStyle.configure("DefaultText.TEntry", foreground="grey") self.ttkStyle.configure("ValidationEntryValid.TEntry", foreground="#4CC417", highlightbackground="#4CC417", highlightcolor="#4CC417", highlightthickness='20') self.ttkStyle.configure("ValidationEntryInvalid.TEntry", foreground="#FF0000", highlightbackground="#FF0000", highlightcolor="#FF0000", highlightthickness='20') self.ttkStyle.configure("ValidationEntryWait.TEntry", foreground="#000000", highlightbackground="#000000", highlightcolor="#000000", highlightthickness='20') self.ttkStyle.configure("ValidationEntryValid.TLabel", foreground="#4CC417") self.ttkStyle.configure("ValidationEntryInvalid.TLabel", foreground="#FF0000") self.ttkStyle.configure("ValidationEntryWait.TLabel", foreground="#000000") self.ttkStyle.configure("Link.TLabel", foreground="#0000ff") self.ttkStyle.configure("LinkOver.TLabel", foreground="#3366ff") #toolbars self.ttkStyle.configure("Toolbar.TFrame") self.ttkStyle.configure("Toolbar.TLabel") self.ttkStyle.configure("Toolbar.TButton", compound=CENTER, padding=0, expand=0)
def setValidationEntry(
self, title, state='valid')
def setValidationEntry(self, title, state="valid"): entry = self.widgetManager.get(WIDGET_NAMES.Entry, title) if not entry.isValidation: self.warn("Entry %s is not a validation entry. Unable to set WAITING VALID.", title) return if state == "wait": col = "#000000" text = '\u2731' eStyle="ValidationEntryWaiting.TEntry" lStyle="ValidationEntryWaiting.TLabel" elif state == "invalid": col = "#FF0000" text = '\u2716' eStyle="ValidationEntryInvalid.TEntry" lStyle="ValidationEntryInvalid.TLabel" elif state == "valid": col = "#4CC417" text = '\u2714' eStyle="ValidationEntryValid.TEntry" lStyle="ValidationEntryValid.TLabel" else: self.warn("Invalid validation state: %s", state) return if not self.ttkFlag: if not entry.showingDefault: entry.config(fg=col) entry.config(highlightbackground=col, highlightcolor=col) entry.config(highlightthickness=1) entry.lab.config(text=text, fg=col) entry.oldFg = col else: if not entry.showingDefault: entry.configure(style=eStyle) entry.lab.config(text=text, style=lStyle) entry.oldFg = eStyle entry.lab.DEFAULT_TEXT = entry.lab.cget("text")
def setValidationEntryLabelBg(
self, title, bg)
def setValidationEntryLabelBg(self, title, bg): ent = self.widgetManager.get(WIDGET_NAMES.Entry, title) if not ent.isValidation: raise Exception("You can only set label BGs on validation entries") ent.lab.config(bg=bg)
def setVisible(
self, visible=True)
def setVisible(self, visible=True): if visible: self.show() else: self.hide()
def show(
self, btn=None)
def show(self, btn=None): self._getTopLevel().displayed = True self._getTopLevel().deiconify()
def showAccess(
self, location=None)
def showAccess(self, location=None): self._makeAccess() # update current settings self.accessOrigFont = self.font self.accessOrigBg = self.bg self.accessOrigFg = self.fg self._resetAccess() self.showSubWindow("access_access_subwindow")
def showAllSubWindows(
self)
def showAllSubWindows(self): for sub in self.widgetManager.group(WIDGET_NAMES.SubWindow): self.showSubWindow(sub)
def showPagedWindowPageNumber(
self, title, show=True)
def showPagedWindowPageNumber(self, title, show=True): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.showPageNumber(show)
def showPagedWindowTitle(
self, title, show=True)
def showPagedWindowTitle(self, title, show=True): pager = self.widgetManager.get(WIDGET_NAMES.PagedWindow, title) pager.showTitle(show)
def showScaleIntervals(
self, title, intervals)
def showScaleIntervals(self, title, intervals): if not self.ttkFlag: sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(tickinterval=intervals) else: self.warn("ttk: showScaleIntervals() not supported: %s", title)
def showScaleValue(
self, title, show=True)
def showScaleValue(self, title, show=True): if not self.ttkFlag: sc = self.widgetManager.get(WIDGET_NAMES.Scale, title) sc.config(showvalue=show) else: self.warn("ttk: showScaleValue() not supported: %s", title)
def showSplash(
self, text='appJar', fill='#FF0000', stripe='#000000', fg='#FFFFFF', font=44)
creates a splash screen to show at start up
def showSplash(self, text="appJar", fill="#FF0000", stripe="#000000", fg="#FFFFFF", font=44): """ creates a splash screen to show at start up """ self.splashConfig= {'text':text, 'fill':fill, 'stripe':stripe, 'fg':fg, 'font':font}
def showSubWindow(
self, title, hide=False, follow=False)
def showSubWindow(self, title, hide=False, follow=False): tl = self.widgetManager.get(WIDGET_NAMES.SubWindow, title) if hide: self.hideAllSubWindows() gui.trace("Showing subWindow %s", title) tl.show() self._bringToFront(tl) tl.block() return tl
def showTabbedFrameTab(
self, title, tab)
def showTabbedFrameTab(self, title, tab): nb = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, title) nb.showTab(tab)
def showTitleBar(
self)
def showTitleBar(self): self.hasTitleBar = True self._doTitleBar()
def showToolbar(
self)
def showToolbar(self): self.tb.show()
def showTreeAttributes(
self, title, show=True)
def showTreeAttributes(self, title, show=True): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) self._loadTooltip() tree.showAttributes(show)
def showTreeMenu(
self, title, show=True)
def showTreeMenu(self, title, show=True): tree = self.widgetManager.get(WIDGET_NAMES.Tree, title) tree.showMenu(show)
def showWidgetType(
self, kind, name)
def showWidgetType(self, kind, name): items = self._getWidgetList(kind, name, limit=False) for item in items: if self._widgetHasContainer(kind, item): gui.trace("Showing widget in container: %s", name) widget = item.master if hasattr(widget, "inContainer") and widget.inContainer: gui.trace("Have container in container") widget = widget.master try: self.widgetManager.get(WIDGET_NAMES.FrameLabel, name).hidden = False except: pass else: msg = "Showing widget" widget = item # only show the widget, if it's not already showing if "in" not in widget.grid_info(): gui.trace("Widget shown: %s", name) widget.grid() # self._updateLabelBoxes(name, widget.grid_info()['column']) else: gui.trace("Showing failed - %s already showing", name)
def shrinkImage(
self, name, x, y='')
def shrinkImage(self, name, x, y=''): label = self.widgetManager.get(WIDGET_NAMES.Image, name) image = label.image.subsample(x, y) label.config(image=image) label.config(anchor=CENTER, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: label.config(background=self._getContainerBg()) label.config(width=image.width(), height=image.height()) label.modImage = image # keep a reference!
def slider(
self, title, *args, **kwargs)
simpleGUI - alternative for scale()
def slider(self, title, *args, **kwargs): """ simpleGUI - alternative for scale() """ return self.scale(title, *args, **kwargs)
def sortGrid(
self, title, columnNumber, descending=False)
def sortGrid(self, title, columnNumber, descending=False): return self.sortTable(title, columnNumber, descending)
def sortTable(
self, title, columnNumber, descending=False)
def sortTable(self, title, columnNumber, descending=False): grid = self.widgetManager.get(WIDGET_NAMES.Table, title) grid.sort(columnNumber, descending)
def soundError(
self)
def soundError(self): self._soundWrap("SystemHand")
def soundWarning(
self)
def soundWarning(self): self._soundWrap("SystemAsterisk")
def spin(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for spinBox()
def spin(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for spinBox() """ return self.spinBox(title, value, *args, **kwargs)
def spinBox(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets spinBoxes all in one go
def spinBox(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets spinBoxes all in one go """ widgKind = WIDGET_NAMES.SpinBox endValue = kwargs.pop("endValue", None) selected = kwargs.pop("selected", None) item = kwargs.pop("item", None) label = kwargs.pop("label", False) # select=select, deselect=<RESET>, toggle=<NONE>, clear=??, rename=set, replace=update, delete=remov if value is None: mode = 'get' else: mode = 'select' mode = kwargs.pop("mode", mode) callFunction = kwargs.pop("callFunction", True) try: self.widgetManager.verify(widgKind, title) except: # widget exists if mode == "select": if value is not None: self.setSpinBoxPos(title, value, *args, **kwargs) else: gui.error("No item specified to select in spinbox: %s", title) elif mode == "toggle": gui.error("%s not available on spinbox: %s", mode, title) elif mode in["clear", "deselect"]: self.clearSpinBox(title) elif mode == "rename": gui.error("%s not implemented yet in spinbox: %s", mode, title) elif mode == "replace": if value is not None: self.changeSpinBox(title, vals=value) else: gui.error("No values specified to replace in spinbox: %s", title) elif mode == "delete": gui.error("%s not implemented yet in spinbox: %s", mode, title) elif mode == "get": pass else: gui.error("Invalid mode (%s) specified in spinbox: %s", mode, title) spinBox = self.getSpinBox(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if endValue is not None: if label: spinBox = self.addLabelSpinBoxRange(title, value, endValue, *args, label=label, **kwargs) else: spinBox = self.addSpinBoxRange(title, value, endValue, *args, **kwargs) else: if label: spinBox = self.addLabelSpinBox(title, value, *args, label=label, **kwargs) else: spinBox = self.addSpinBox(title, value, *args, **kwargs) if selected is not None: self.setSpinBoxPos(title, selected) if item is not None: self.setSpinBox(title, item) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return spinBox
def spinbox(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for spinBox()
def spinbox(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for spinBox() """ return self.spinBox(title, value, *args, **kwargs)
def startAnimation(
self, name)
def startAnimation(self, name): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image if not img.animating: img.animating = True anim_id = self.topLevel.after(img.anim_speed, self._animateImage, name) self.widgetManager.update(WIDGET_NAMES.AnimationID, name, anim_id)
def startContainer(
self, fType, title, row=None, column=0, colspan=0, rowspan=0, sticky=None, name=None)
def startContainer(self, fType, title, row=None, column=0, colspan=0, rowspan=0, sticky=None, name=None): if name is None: name = title if fType == WIDGET_NAMES.LabelFrame: # first, make a LabelFrame, and position it correctly self.widgetManager.verify(WIDGET_NAMES.LabelFrame, title) if not self.ttkFlag: container = LabelFrame(self.getContainer(), text=name, relief="groove") container.config(background=self._getContainerBg(), font=self._getContainerProperty('labelFont')) else: container = ttk.LabelFrame(self.getContainer(), text=name, relief="groove") container.DEFAULT_TEXT = name container.isContainer = True self.setPadX(5) self.setPadY(5) self._positionWidget(container, row, column, colspan, rowspan, "nsew") self.widgetManager.add(WIDGET_NAMES.LabelFrame, title, container) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.LabelFrame, container, 0, 1, sticky) return container elif fType == WIDGET_NAMES.Canvas: # first, make a canvas, and position it correctly self.widgetManager.verify(WIDGET_NAMES.Canvas, title) container = Canvas(self.getContainer()) container.isContainer = True self._positionWidget(container, row, column, colspan, rowspan, "nsew") self.widgetManager.add(WIDGET_NAMES.Canvas, title, container) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Canvas, container, 0, 1, "") return container elif fType == WIDGET_NAMES.TabbedFrame: self.widgetManager.verify(WIDGET_NAMES.TabbedFrame, title) tabbedFrame = self._tabbedFrameMaker(self.getContainer(), self.ttkFlag, font=self._getContainerProperty('labelFont')) if not self.ttkFlag: tabbedFrame.config(bg=self._getContainerBg()) tabbedFrame.isContainer = True self._positionWidget( tabbedFrame, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.TabbedFrame, title, tabbedFrame) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.TabbedFrame, tabbedFrame, 0, 1, sticky) return tabbedFrame elif fType == WIDGET_NAMES.Tab: # add to top of stack self.containerStack[-1]['widgets'] = True tabTitle = self._getContainerProperty('title') + "__" + title tab = self._getContainerProperty('container').addTab(title) self._addContainer(tabTitle, WIDGET_NAMES.Tab, tab, 0, 1, sticky) return tab elif fType == WIDGET_NAMES.Notebook: if not self.ttkFlag: raise Exception("Cannot create a ttk Notebook, unless ttk is enabled.") self.widgetManager.verify(WIDGET_NAMES.Notebook, title) notebook = ttk.Notebook(self.getContainer()) tabbedFrame.isContainer = True self._positionWidget( notebook, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.Notebook, title, notebook) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Notebook, notebook, 0, 1, sticky) return notebook elif fType == WIDGET_NAMES.Note: # add to top of stack self.containerStack[-1]['widgets'] = True noteTitle = self._getContainerProperty('title') + "__" + title frame = ttk.Frame(self._getContainerProperty('container')) self._getContainerProperty('container').add(frame, text=title) self._addContainer(noteTitle, WIDGET_NAMES.Note, frame, 0, 1, sticky) return frame elif fType == WIDGET_NAMES.PanedFrame: # if we previously put a frame for widgets # remove it if self._getContainerProperty('type') == WIDGET_NAMES.Pane: self.stopContainer() # now, add the new pane self.widgetManager.verify(WIDGET_NAMES.PanedFrame, title) pane = PanedWindow( self.getContainer(), showhandle=True, sashrelief="groove", bg=self._getContainerBg()) pane.isContainer = True self._positionWidget( pane, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.PanedFrame, title, pane) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.PanedFrame, pane, 0, 1, sticky) # now, add a frame to the pane self.startContainer(WIDGET_NAMES.Pane, title) return pane elif fType == WIDGET_NAMES.Pane: # create a frame, and add it to the pane pane = Pane(self.getContainer(), bg=self._getContainerBg()) pane.isContainer = True self._getContainerProperty('container').add(pane) self.widgetManager.add(WIDGET_NAMES.Pane, title, pane) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Pane, pane, 0, 1, sticky) return pane elif fType == WIDGET_NAMES.ScrollPane: self.widgetManager.verify(WIDGET_NAMES.ScrollPane, title) # naned used to diabled sctollbars if name not in ["horizontal", "vertical", ""]: gui.warn("ScrollPane %s: Invalid value for disabled, must be one of 'horizontal' or 'vertical'", title) scrollPane = ScrollPane(self.getContainer(), disabled=name) if not self.ttkFlag: scrollPane.config(bg=self._getContainerBg()) scrollPane.isContainer = True self._positionWidget( scrollPane, row, column, colspan, rowspan, sticky=sticky) self.widgetManager.add(WIDGET_NAMES.ScrollPane, title, scrollPane) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.ScrollPane, scrollPane, 0, 1, sticky) return scrollPane elif fType == WIDGET_NAMES.ToggleFrame: self.widgetManager.verify(WIDGET_NAMES.ToggleFrame, title) toggleFrame = ToggleFrame(self.getContainer(), title=title, bg=self._getContainerBg()) toggleFrame.configure(font=self._getContainerProperty('labelFont')) toggleFrame.isContainer = True self._positionWidget( toggleFrame, row, column, colspan, rowspan, sticky=sticky) self._addContainer(title, WIDGET_NAMES.ToggleFrame, toggleFrame, 0, 1, "nw") self.widgetManager.add(WIDGET_NAMES.ToggleFrame, title, toggleFrame) return toggleFrame elif fType == WIDGET_NAMES.PagedWindow: # create the paged window pagedWindow = PagedWindow(self.getContainer(), title=title, bg=self._getContainerBg(), width=200, height=400, buttonFont=self._getContainerProperty('buttonFont'), titleFont=self._getContainerProperty('labelFont')) # bind events self.topLevel.bind("<Left>", pagedWindow.showPrev) self.topLevel.bind("<Control-Left>", pagedWindow.showFirst) self.topLevel.bind("<Right>", pagedWindow.showNext) self.topLevel.bind("<Control-Right>", pagedWindow.showLast) # register it as a container pagedWindow.isContainer = True self._positionWidget(pagedWindow, row, column, colspan, rowspan, sticky=sticky) self._addContainer(title, WIDGET_NAMES.PagedWindow, pagedWindow, 0, 1, "nw") self.widgetManager.add(WIDGET_NAMES.PagedWindow, title, pagedWindow) return pagedWindow elif fType == WIDGET_NAMES.Page: page = self._getContainerProperty('container').addPage() page.isContainer = True self._addContainer(title, WIDGET_NAMES.Page, page, 0, 1, sticky) self.containerStack[-1]['expand'] = "None" return page elif fType == WIDGET_NAMES.FrameStack: # create the paged window frameStack = FrameStack(self.getContainer(), bg=self._getContainerBg()) self.widgetManager.add(WIDGET_NAMES.FrameStack, title, frameStack) # register it as a container frameStack.isContainer = True self._positionWidget(frameStack, row, column, colspan, rowspan, sticky=sticky) self._addContainer(title, WIDGET_NAMES.FrameStack, frameStack, 0, 1, "news") return frameStack elif fType == WIDGET_NAMES.Frame: # first, make a Frame, and position it correctly self.widgetManager.verify(WIDGET_NAMES.Frame, title) container = self._makeAjFrame()(self.getContainer()) container.isContainer = True container.config(background=self._getContainerBg()) self._positionWidget( container, row, column, colspan, rowspan, "nsew") self.widgetManager.add(WIDGET_NAMES.Frame, title, container) # now, add to top of stack self._addContainer(title, WIDGET_NAMES.Frame, container, 0, 1, sticky) return container elif fType == WIDGET_NAMES.SubFrame: subFrame = self._getContainerProperty('container').addFrame() subFrame.isContainer = True self._addContainer(title, WIDGET_NAMES.SubFrame, subFrame, 0, 1, "news") self.widgetManager.add(WIDGET_NAMES.Frame, title, subFrame) return subFrame else: raise Exception("Unknown container: " + fType)
def startFrame(
self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')
def startFrame(self, title=None, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): frameType = WIDGET_NAMES.Frame if self._getContainerProperty('type') == WIDGET_NAMES.FrameStack: # generate a frame title frameNum = self._getContainerProperty('container').getNumFrames() title = self._getContainerProperty('title') + "__" + str(frameNum) gui.trace("Adding new subFrame: %s", title) self.containerStack[-1]['widgets'] = True frameType = WIDGET_NAMES.SubFrame else: if title is None: raise Exception("All frames must have a title") gui.trace("Adding new frame: %s", title) return self.startContainer(frameType, title, row, column, colspan, rowspan, sticky)
def startFrameStack(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='news', change=None, start=-1)
def startFrameStack(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="news", change=None, start=-1): fs = self.startContainer(WIDGET_NAMES.FrameStack, title, row, column, colspan, rowspan, sticky) fs.setChangeFunction(change) fs.setStartFrame(start) return fs
def startLabelFrame(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='w', hideTitle=False, label=None, name=None)
def startLabelFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky=W, hideTitle=False, label=None, name=None): if label is not None: name = label if hideTitle: name = '' lf = self.startContainer(WIDGET_NAMES.LabelFrame, title, row, column, colspan, rowspan, sticky, name) return lf
def startNote(
self, title)
def startNote(self, title): # auto close the previous TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Note: self.warn("You didn't STOP the previous NOTE") self.stopContainer() elif self._getContainerProperty('type') != WIDGET_NAMES.Notebook: raise Exception( "Can't add a Note to the current container: ", self._getContainerProperty('type')) return self.startContainer(WIDGET_NAMES.Note, title)
def startNotebook(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')
def startNotebook(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): return self.startContainer(WIDGET_NAMES.Notebook, title, row, column, colspan, rowspan, sticky)
def startPage(
self, sticky='nw')
def startPage(self, sticky="nw"): if self._getContainerProperty('type') == WIDGET_NAMES.Page: self.warn("You didn't STOP the previous PAGE") self.stopPage() elif self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow: raise Exception("Can't start a PAGE, currently in:", self._getContainerProperty('type')) self.containerStack[-1]['widgets'] = True # generate a page title pageNum = self._getContainerProperty('container').frameStack.getNumFrames() + 1 pageTitle = self._getContainerProperty('title') + "__" + str(pageNum) return self.startContainer(WIDGET_NAMES.Page, pageTitle, row=None, column=None, colspan=None, rowspan=None, sticky=sticky)
def startPagedWindow(
self, title, row=None, column=0, colspan=0, rowspan=0)
def startPagedWindow(self, title, row=None, column=0, colspan=0, rowspan=0): return self.startContainer( WIDGET_NAMES.PagedWindow, title, row, column, colspan, rowspan, sticky="nsew")
def startPanedFrame(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')
def startPanedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): p = self.startContainer(WIDGET_NAMES.PanedFrame, title, row, column, colspan, rowspan, sticky) return p
def startPanedFrameVertical(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')
def startPanedFrameVertical( self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): p = self.startPanedFrame(title, row, column, colspan, rowspan, sticky) self.setPanedFrameVertical(title) return p
def startScrollPane(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW', disabled='')
def startScrollPane(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", disabled=""): return self.startContainer(WIDGET_NAMES.ScrollPane, title, row, column, colspan, rowspan, sticky, disabled)
def startSubWindow(
self, name, title=None, modal=False, blocking=False, transient=False, grouped=True)
def startSubWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True): self.widgetManager.verify(WIDGET_NAMES.SubWindow, name) gui.trace("Starting subWindow %s", name) top = SubWindow(self, self.topLevel, name, title=title, stopFunc = self.confirmHideSubWindow, modal=modal, blocking=blocking, transient=transient, grouped=grouped) ico = self._getTopLevel().winIcon self.widgetManager.add(WIDGET_NAMES.SubWindow, name, top) # now, add to top of stack self._addContainer(name, WIDGET_NAMES.SubWindow, top, 0, 1, "") # add an icon if required if ico is not None: self.setIcon(ico) else: top.winIcon = None return top
def startTab(
self, title, beforeTab=None, afterTab=None)
def startTab(self, title, beforeTab=None, afterTab=None): if beforeTab is not None and afterTab is not None: self.warn("You can't specify a before and after value for tab: %s", title) beforeTab = afterTab = None # auto close the previous TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Tab: self.warn("You didn't STOP the previous TAB") self.stopContainer() elif self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame: raise Exception("Can't add a Tab to the current container: ", self._getContainerProperty('type')) tf = self.widgetManager.get(WIDGET_NAMES.TabbedFrame, self._getContainerProperty("title")) tf.setBeforeTab(beforeTab) tf.setAfterTab(afterTab) tab = self.startContainer(WIDGET_NAMES.Tab, title) tf.setBeforeTab() tf.setAfterTab() return tab
def startTabbedFrame(
self, title, row=None, column=0, colspan=0, rowspan=0, sticky='NSEW')
def startTabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW"): return self.startContainer(WIDGET_NAMES.TabbedFrame, title, row, column, colspan, rowspan, sticky)
def startToggleFrame(
self, title, row=None, column=0, colspan=0, rowspan=0)
def startToggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0): return self.startContainer(WIDGET_NAMES.ToggleFrame, title, row, column, colspan, rowspan, sticky="new")
def status(
self, *args, **kwargs)
def status(self, *args, **kwargs): self.statusbar(*args, **kwargs)
def statusbar(
self, *args, **kwargs)
simpleGUI - shortener for statusbar
def statusbar(self, *args, **kwargs): """ simpleGUI - shortener for statusbar """ bg = kwargs.pop('bg', None) fg = kwargs.pop('fg', None) width = kwargs.pop('width', None) text = kwargs.pop('text', "") header = kwargs.pop('header', None) fields = kwargs.pop('fields', 1) field = kwargs.pop('field', 0) side = kwargs.pop('side', None) if not self.hasStatus: self.addStatusbar(header=header, fields=fields, side=side) self.setStatusbar(text=text) else: if len(args) > 0: text = args[0] if len(args) > 1: field = args[1] if header is not None: self.setStatusbarHeader(header) self.setStatusbar(text=text, field=field) if bg is not None: self.setStatusbarBg(bg) if fg is not None: self.setStatusbarFg(fg) if width is not None: self.setStatusbarWidth(width)
def stop(
self, event=None)
Closes the GUI. If a stop function is set, will only close the GUI if True
def stop(self, event=None): """ Closes the GUI. If a stop function is set, will only close the GUI if True """ theFunc = self._getTopLevel().stopFunction if theFunc is None or theFunc(): if self.useSettings: self.saveSettings(self.settingsFile) # stop the after loops self.alive = False self.topLevel.after_cancel(self.pollId) self.topLevel.after_cancel(self.flashId) if self.preloadAnimatedImageId: self.topLevel.after_cancel(self.preloadAnimatedImageId) if self.processQueueId: self.topLevel.after_cancel(self.processQueueId) # stop any animations for key in self.widgetManager.group(WIDGET_NAMES.AnimationID): self.topLevel.after_cancel(self.widgetManager.get(WIDGET_NAMES.AnimationID, key)) # stop any maps for key in self.widgetManager.group(WIDGET_NAMES.Map): self.widgetManager.get(WIDGET_NAMES.Map, key).stopUpdates() # stop any sounds, ignore error when not on Windows try: self.stopSound() except: pass self.topLevel.quit() if not self.fastStop: self.topLevel.destroy() self.__class__.instantiated = False gui.info("--- GUI stopped ---")
def stopAllPanedFrames(
self)
def stopAllPanedFrames(self): while True: try: self.stopPanedFrame() except: break
def stopAnimation(
self, name)
def stopAnimation(self, name): img = self.widgetManager.get(WIDGET_NAMES.Image, name).image img.animating = False
def stopContainer(
self)
def stopContainer(self): self._removeContainer()
def stopFrame(
self)
def stopFrame(self): if self._getContainerProperty('type') not in [WIDGET_NAMES.Frame, WIDGET_NAMES.SubFrame]: raise Exception("Can't stop a FRAME, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopFrameStack(
self)
def stopFrameStack(self): if self._getContainerProperty('type') != WIDGET_NAMES.FrameStack: raise Exception("Can't stop a FRAMESTACK, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopLabelFrame(
self)
def stopLabelFrame(self): if self._getContainerProperty('type') != WIDGET_NAMES.LabelFrame: raise Exception("Can't stop a LABELFRAME, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopNote(
self)
def stopNote(self): if self._getContainerProperty('type') != WIDGET_NAMES.Note: raise Exception("Can't stop a NOTE, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopNotebook(
self)
def stopNotebook(self): # auto close the existing TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Note: self.warn("You didn't STOP the previous NOTE") self.stopContainer() self.stopContainer()
def stopPage(
self)
def stopPage(self): if self._getContainerProperty('type') == WIDGET_NAMES.Page: self.stopContainer() else: raise Exception("Can't stop PAGE, currently in:", self._getContainerProperty('type'))
def stopPagedWindow(
self)
def stopPagedWindow(self): if self._getContainerProperty('type') == WIDGET_NAMES.Page: self.warn("You didn't STOP the previous PAGE") self.stopPage() if self._getContainerProperty('type') != WIDGET_NAMES.PagedWindow: raise Exception("Can't stop a PAGEDWINDOW, currently in:", self._getContainerProperty('type')) self._getContainerProperty('container').stopPagedWindow() self.stopContainer()
def stopPanedFrame(
self)
def stopPanedFrame(self): if self._getContainerProperty('type') == WIDGET_NAMES.Pane: self.stopContainer() if self._getContainerProperty('type') != WIDGET_NAMES.PanedFrame: raise Exception("Can't stop a PANEDFRAME, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopScrollPane(
self)
def stopScrollPane(self): if self._getContainerProperty('type') != WIDGET_NAMES.ScrollPane: raise Exception("Can't stop a SCROLLPANE, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopSound(
self)
def stopSound(self): self._soundWrap(None)
def stopSubWindow(
self)
def stopSubWindow(self): container = self.containerStack[-1] if container['type'] == WIDGET_NAMES.SubWindow: if not hasattr(container["container"], 'ms'): self.setMinSize(container["container"]) self.stopContainer() else: raise Exception("Can't stop a SUBWINDOW, currently in:", self._getContainerProperty('type'))
def stopTab(
self)
def stopTab(self): if self._getContainerProperty('type') != WIDGET_NAMES.Tab: raise Exception("Can't stop a TAB, currently in:", self._getContainerProperty('type')) self.stopContainer()
def stopTabbedFrame(
self)
def stopTabbedFrame(self): # auto close the existing TAB - keep it? if self._getContainerProperty('type') == WIDGET_NAMES.Tab: self.warn("You didn't STOP the previous TAB") self.stopContainer() self.stopContainer()
def stopToggleFrame(
self)
def stopToggleFrame(self): if self._getContainerProperty('type') != WIDGET_NAMES.ToggleFrame: raise Exception("Can't stop a TOGGLEFRAME, currently in:", self._getContainerProperty('type')) self._getContainerProperty('container').stop() self.stopContainer()
def stringBox(
self, title, message, parent=None)
def stringBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return SimpleDialog.askstring(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return SimpleDialog.askstring(title=title, message=message, **opts)
def subWindow(
*args, **kwds)
@contextmanager def subWindow(self, name, title=None, modal=False, blocking=False, transient=False, grouped=True, **kwargs): visible = kwargs.pop("visible", None) try: sw = self.startSubWindow(name, title, modal, blocking, transient, grouped) except ItemLookupError: sw = self.openSubWindow(name) self.configure(**kwargs) try: yield sw finally: self.stopSubWindow() if visible is True: self.showSubWindow(name)
def tab(
*args, **kwds)
@contextmanager def tab(self, title, tabTitle=None, **kwargs): beforeTab = kwargs.pop("beforeTab", None) afterTab = kwargs.pop("afterTab", None) if tabTitle is None: try: tab = self.startTab(title, beforeTab, afterTab) except ItemLookupError: if self._getContainerProperty('type') != WIDGET_NAMES.TabbedFrame: raise Exception("Can't open a Tab in the current container: ", self._getContainerProperty('type')) else: tabTitle = self._getContainerProperty('title') tab = self.openTab(tabTitle, title) else: tab = self.openTab(title, tabTitle) self.configure(**kwargs) try: yield tab finally: self.stopTab()
def tabbedFrame(
*args, **kwds)
@contextmanager def tabbedFrame(self, title, row=None, column=0, colspan=0, rowspan=0, sticky="NSEW", **kwargs): try: tabs = self.startTabbedFrame(title, row, column, colspan, rowspan, sticky) except ItemLookupError: tabs = self.openTabbedFrame(title) command = kwargs.pop("change", None) if command is not None: self.setTabbedFrameChangeCommand(title, command) self.configure(**kwargs) try: yield tabs finally: self.stopTabbedFrame()
def table(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets tables all in one go
def table(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets tables all in one go """ widgKind = WIDGET_NAMES.Table kind = kwargs.pop("kind", 'normal') action=kwargs.pop('action', None) addRow=kwargs.pop('addRow', None) actionHeading=kwargs.pop('actionHeading', "Action") actionButton=kwargs.pop('actionButton', "Press") addButton=kwargs.pop('addButton', "Add") showMenu=kwargs.pop('showMenu', False) horiz=kwargs.pop('horizontal', True) change=kwargs.pop('change', None) edit=kwargs.pop('edit', None) try: self.widgetManager.verify(widgKind, title) except: # widget exists if value is not None: self.replaceAllTableRows(title, value) table = self.getTableEntries(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if kind == 'normal': table = self.addTable(title, value, *args, action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton, addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs ) else: table = self.addDbTable(title, value, *args, action=action, addRow=addRow, actionHeading=actionHeading, actionButton=actionButton, addButton=addButton, showMenu=showMenu, horizontal=horiz, **kwargs ) if change is not None: self.setTableChangeFunction(title, change) if edit is not None: self.setTableEditFunction(title, edit) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return table
def text(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for textArea()
def text(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for textArea() """ return self.textArea(title, value, *args, **kwargs)
def textArea(
self, title, value=None, *args, **kwargs)
adds, sets & gets textAreas all in one go
def textArea(self, title, value=None, *args, **kwargs): """ adds, sets & gets textAreas all in one go """ widgKind = WIDGET_NAMES.TextArea scroll = kwargs.pop("scroll", False) end = kwargs.pop("end", True) replace = kwargs.pop("replace", False) callFunction = kwargs.pop("callFunction", True) disabled = kwargs.pop("disabled", False) tag = kwargs.pop("tag", None) tags = kwargs.pop("tags", []) try: self.widgetManager.verify(WIDGET_NAMES.TextArea, title) except: # widget exists text = self.getTextArea(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) if scroll: text = self._textMaker(title, "scroll", *args, **kwargs) else: text = self._textMaker(title, "text", *args, **kwargs) callFunction = False # create any tags for _tag in tags: self.textAreaCreateTag(title, _tag[0], **_tag[1]) if replace: self.clearTextArea(title) if value is not None: self.setTextArea(title, value, end=end, callFunction=callFunction, tag=tag) if disabled: self.disableTextArea(title) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) return text
def textAreaApplyFontRange(
self, title, tag, start, end='end')
removes the tag from the specified range
def textAreaApplyFontRange(self, title, tag, start, end=END): """removes the tag from the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) tag = ta.verifyFontTag(tag) if tag != "UNDERLINE": ta.tag_remove("AJ_BOLD", start, end) ta.tag_remove("AJ_ITALIC", start, end) ta.tag_remove("AJ_BOLD_ITALIC", start, end) ta.tag_add("AJ_" + tag, start, end)
def textAreaApplyFontSelected(
self, title, tag)
def textAreaApplyFontSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaApplyFontRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()
def textAreaChangeTag(
self, title, name, **kwargs)
changes a tag on the specified text area
def textAreaChangeTag(self, title, name, **kwargs): """ changes a tag on the specified text area """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_config(name, **kwargs)
def textAreaChanged(
self, title)
Creates a temporary md5 hash - and compares it with a previously generated & stored hash The previous hash has to be generated manually, by calling logTextArea
:param title: the TextArea to hash :returns: bool - True if the TextArea has changed or False if it hasn't :raises ItemLookupError: if the title can't be found
def textAreaChanged(self, title): """ Creates a temporary md5 hash - and compares it with a previously generated & stored hash The previous hash has to be generated manually, by calling logTextArea :param title: the TextArea to hash :returns: bool - True if the TextArea has changed or False if it hasn't :raises ItemLookupError: if the title can't be found """ self._loadHashlib() if hashlib is False: self.warn("Unable to log TextArea, hashlib library not available") else: text = self.widgetManager.get(WIDGET_NAMES.TextArea, title) return text.__hash != text.getTextAreaHash()
def textAreaCreateTag(
self, title, name, **kwargs)
creates a new tag on the specified text area
def textAreaCreateTag(self, title, name, **kwargs): """ creates a new tag on the specified text area """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_config(name, **kwargs)
def textAreaDeleteTag(
self, title, *tags)
deletes the specified tag
def textAreaDeleteTag(self, title, *tags): """ deletes the specified tag """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_delete(*tags)
def textAreaTagPattern(
self, title, tag, pattern, regexp=False)
applies the tag to the specified text
def textAreaTagPattern(self, title, tag, pattern, regexp=False): """ applies the tag to the specified text """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.highlightPattern(pattern, tag, regexp=regexp)
def textAreaTagRange(
self, title, tag, start, end='end')
applies the tag to the specified range
def textAreaTagRange(self, title, tag, start, end=END): """ applies the tag to the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_add(tag, start, end)
def textAreaTagSelected(
self, title, tag)
def textAreaTagSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaTagRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()
def textAreaToggleFontRange(
self, title, tag, start, end='end')
will toggle the tag at the specified range
def textAreaToggleFontRange(self, title, tag, start, end=END): """ will toggle the tag at the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) tag = ta.verifyFontTag(tag) if tag in ta.tag_names(start): ta.tag_remove("AJ_"+tag, start, end) else: self.textAreaApplyFontRange(title, tag, start, end)
def textAreaToggleFontSelected(
self, title, tag)
def textAreaToggleFontSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaToggleFontRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()
def textAreaToggleTagRange(
self, title, tag, start, end='end')
will toggle the tag at the specified range
def textAreaToggleTagRange(self, title, tag, start, end=END): """ will toggle the tag at the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) if tag in ta.tag_names(start): self.textAreaUntagRange(title, tag, start, end) else: self.textAreaTagRange(title, tag, start, end)
def textAreaToggleTagSelected(
self, title, tag)
def textAreaToggleTagSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaToggleTagRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()
def textAreaUntagRange(
self, title, tag, start, end='end')
removes the tag from the specified range
def textAreaUntagRange(self, title, tag, start, end=END): """removes the tag from the specified range """ ta = self.widgetManager.get(WIDGET_NAMES.TextArea, title) ta.tag_remove(tag, start, end)
def textAreaUntagSelected(
self, title, tag)
def textAreaUntagSelected(self, title, tag): if self.widgetManager.get(WIDGET_NAMES.TextArea, title).tag_ranges(SEL): self.textAreaUntagRange(title, tag, SEL_FIRST, SEL_LAST) self.widgetManager.get(WIDGET_NAMES.TextArea, title).focus_set()
def textBox(
self, title='Text Box', question='Enter text', defaultValue=None, parent=None)
def textBox(self, title="Text Box", question="Enter text", defaultValue=None, parent=None): self.topLevel.update_idletasks() if defaultValue is not None: defaultVar = StringVar(self.topLevel) defaultVar.set(defaultValue) else: defaultVar = None if parent is None: parent = self.topLevel else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) return TextDialog(parent, title, question, defaultVar=defaultVar).result
def thread(
self, func, *args, **kwargs)
will run the supplied function in a separate thread
param func: the function to run
def thread(self, func, *args, **kwargs): """ will run the supplied function in a separate thread param func: the function to run """ self._loadThreading() if Queue is False: gui.warn("Unable to queueFunction - threading not possible.") else: t = Thread(group=None, target=func, name=None, args=args, kwargs=kwargs) t.daemon = True t.start()
def threadCallback(
self, func, callback, *args, **kwargs)
Run a given method in a new thread with passed arguments. When func completes call the callback with the result.
:param func: Method that returns the result. :param callback: Method that receives the result. :param args: Positional arguments for func. :param kwargs: Keyword args for func.
def threadCallback(self, func, callback, *args, **kwargs): """Run a given method in a new thread with passed arguments. When func completes call the callback with the result. :param func: Method that returns the result. :param callback: Method that receives the result. :param args: Positional arguments for func. :param kwargs: Keyword args for func. """ def innerThread(func, callback, *args, **kwargs): result = func(*args, **kwargs) self.queueFunction(callback, result) if not callable(func) or not callable(callback): gui.error("Function (or callback) method isn't callable!") return self.thread(innerThread, func, callback, *args, **kwargs)
def tick(
self, title, value=None, *args, **kwargs)
simpleGUI - shortner for checkBox()
def tick(self, title, value=None, *args, **kwargs): """ simpleGUI - shortner for checkBox() """ return self.checkBox(title, value, *args, **kwargs)
def toggleFrame(
*args, **kwds)
@contextmanager def toggleFrame(self, title, row=None, column=0, colspan=0, rowspan=0, **kwargs): try: tog = self.startToggleFrame(title, row, column, colspan, rowspan) except ItemLookupError: tog = self.openToggleFrame(title) self.configure(**kwargs) try: yield tog finally: self.stopToggleFrame()
def toggleToggleFrame(
self, title)
def toggleToggleFrame(self, title): toggle = self.widgetManager.get(WIDGET_NAMES.ToggleFrame, title) toggle.toggle()
def toolbar(
self, names, funcs, **kwargs)
simpleGUI - shortener for toolbar
def toolbar(self, names, funcs, **kwargs): """ simpleGUI - shortener for toolbar """ icons = kwargs.pop('icons', kwargs.pop('findIcon', False)) pinned = kwargs.pop('pinned', None) disabled = kwargs.pop('disabled', None) hidden = kwargs.pop('hidden', None) status = kwargs.pop('status', None) bg = kwargs.pop('bg', None) if bg is not None: self.setToolbarBg(bg) self.addToolbar(names, funcs, findIcon=icons is not False) # allow status and icon name to be passed in a list for x, n in enumerate(names): if icons is not None: try: self.setToolbarIcon(n, icons[x]) except: pass if status is not None: try: self.setToolbarButtonDisabled(n, not status[x]) except: pass if pinned is not None: self.setToolbarPinned(pinned=pinned) if disabled is not None: self.setToolbarDisabled(disabled=disabled) if hidden is True: self.hideToolbar()
def trace(
message, *args)
wrapper for logMessage - setting level to TRACE
@staticmethod def trace(message, *args): """ wrapper for logMessage - setting level to TRACE """ gui.logMessage(message, "TRACE", *args)
def translate(
self, key, default=None)
returns a translated version of the key, using the current language if none found, returns the default value
def translate(self, key, default=None): """ returns a translated version of the key, using the current language if none found, returns the default value """ return self._translate(key, "EXTERNAL", default)
def tree(
self, title, value=None, *args, **kwargs)
simpleGUI - adds, sets & gets trees all in one go
def tree(self, title, value=None, *args, **kwargs): """ simpleGUI - adds, sets & gets trees all in one go """ widgKind = WIDGET_NAMES.Tree click = kwargs.pop("click", None) dblClick = kwargs.pop("dbl", None) edit = kwargs.pop("edit", None) editable = kwargs.pop("editable", None) showAttr = kwargs.pop("attributes", None) showMenu = kwargs.pop("menu", None) fg = kwargs.pop("fg", None) bg = kwargs.pop("bg", None) fgH = kwargs.pop("fgH", None) bgH = kwargs.pop("bgH", None) try: self.widgetManager.verify(widgKind, title) except: # widget exists tree = self.getTree(title) else: # new widget kwargs = self._parsePos(kwargs.pop("pos", []), kwargs) tree = self.addTree(title, value, *args, **kwargs) if len(kwargs) > 0: self._configWidget(title, widgKind, **kwargs) self.setTreeColours(title, fg, bg, fgH, bgH) if click is not None: self.setTreeClickFunction(title, click) if edit is not None: self.setTreeEditFunction(title, edit) if dblClick is not None: self.setTreeDoubleClickFunction(title, dblClick) if editable is not None: self.setTreeEditable(title, editable) if showAttr is not None: self.showTreeAttributes(title, showAttr) if showMenu is not None: self.showTreeMenu(title, showMenu) return tree
def unbindKey(
self, key)
unbinds the specified key from whatever functions it is bound to
def unbindKey(self, key): """ unbinds the specified key from whatever functions it is bound to """ if key[0] == "<": gui.warn("Shortcuts should not include chevrons: %s", key) key= key[1:-1] self.widgetManager.get(WIDGET_NAMES.Bindings, key).removeBindings() self.widgetManager.remove(WIDGET_NAMES.Bindings, key)
def unbindKeys(
self, keys)
unbinds the specified keys from whatever functions they are bound to
def unbindKeys(self, keys): """ unbinds the specified keys from whatever functions they are bound to """ for key in keys: self.unbindKey(key)
def updateListBox(
self, title, items, select=False, callFunction=True)
def updateListBox(self, title, items, select=False, callFunction=True): self.clearListBox(title, callFunction=callFunction) self.addListItems(title, items, select=select)
def updatePlot(
self, title, t, s, keepLabels=False)
def updatePlot(self, title, t, s, keepLabels=False): axes = self.widgetManager.get(WIDGET_NAMES.Plot, title).axes if keepLabels: xLab = axes.get_xlabel() yLab = axes.get_ylabel() pTitle = axes.get_title() handles, legends = axes.get_legend_handles_labels() axes.clear() axes.plot(t, s) if keepLabels: axes.set_xlabel(xLab) axes.set_ylabel(yLab) axes.set_title(pTitle) axes.legend(handles, legends) self.refreshPlot(title) return axes
def warn(
message, *args)
wrapper for logMessage - setting level to WARNING
@staticmethod def warn(message, *args): """ wrapper for logMessage - setting level to WARNING """ gui.logMessage(message, "WARNING", *args)
def warningBox(
self, title, message, parent=None)
def warningBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: MessageBox.showwarning(title, message) if self.topLevel.displayed: self._bringToFront() else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} MessageBox.showwarning(title, message, **opts) self._bringToFront(parent)
def yesNoBox(
self, title, message, parent=None)
def yesNoBox(self, title, message, parent=None): self.topLevel.update_idletasks() if parent is None: return MessageBox.askyesno(title, message) else: parent = self.widgetManager.get(WIDGET_NAMES.SubWindow, parent) opts = {"parent": parent} return MessageBox.askyesno(title=title, message=message, **opts)
def zoomGoogleMap(
self, title, mod)
def zoomGoogleMap(self, title, mod): gMap = self.widgetManager.get(WIDGET_NAMES.Map, title) if mod in ["+", "-"]: gMap.zoom(mod) elif isinstance(mod, int) and 0 <= mod <= 22: gMap.setZoom(mod)
def zoomImage(
self, name, x, y='')
def zoomImage(self, name, x, y=''): if x <= 0: self.shrinkImage(name, x * -1, y * -1) else: self.growImage(name, x, y)
Instance variables
var EVENT_SIZE
var EVENT_SPEED
var Widgets
var accessMade
var alive
var appJarIcon
var appWindow
var bg
var built
var colspan
var configParser
var containerStack
var copyAndPaste
var dnd
var doFlash
var editMenu
var enterKey
var events
var expand
var externalSettings
var fastStop
var fg
var font
var fonts
var fullscreen
var guiPadding
var hasMenu
var hasStatus
var hasTitleBar
var icon
var icon_path
var inPadding
var inputFont
var labelFont
var language
returns the current language
var location
var logFile
var logLevel
var padding
var platform
var pollTime
var preloadAnimatedImageId
var processQueueId
var randomColour
generates a random colour
var resizable
var resource_path
var row
var rowspan
var settingsFile
var size
var sound_path
var splashConfig
var startFunction
var startWindow
var statusFont
var sticky
var stopFunction
var stretch
var tableFont
var tb
var title
var top
var topLevel
var translations
var transparency
var ttkFlag
var ttkTheme
var useSettings
var userImages
var userSounds
var validateNumeric
var validateSpinBox
var visible
var widgetManager